[Canonical page](<https://docs.dhtmlx.com/planning-engine/java/api/project/validation/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Validation

Selects how a [`Project`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project.md>) treats invalid [`ProjectData`](<https://docs.dhtmlx.com/planning-engine/java/api/data/project-data.md>): reject it with an exception, or coerce it into valid data. Set it through the `validation` field of [`ProjectOptions`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project-options.md>). It applies to the data passed to [`Project#solve`](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve.md>) and [`Project#analyze`](<https://docs.dhtmlx.com/planning-engine/java/api/project-analyze.md>).

## Declaration

```java
package org.dhtmlx.plen;

public enum Validation {
  STRICT,
  REPAIR
}
```

## Variants

| Variant | Description |
|---|---|
| `STRICT` | Default. Invalid input throws [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>) with the path of the bad field. The rules are listed on [`ProjectData`](<https://docs.dhtmlx.com/planning-engine/java/api/data/project-data.md>). |
| `REPAIR` | Fixes the input listed below before scheduling and reports each fix as a [`Note.Repaired`](<https://docs.dhtmlx.com/planning-engine/java/api/results/note.md>) entry in [`ScheduleResult.notes`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md>). Input it can't fix still throws `InputException`. The fixes are made on the project's own copy, so the caller's `ProjectData` isn't modified. |

## What REPAIR changes

Each row is one fix. The note's `path` uses wire spelling and points at the field that changed. For duplicate ids the index is the record's position in the input. For the other fixes it's the position after duplicates are removed.

| Input | Fix | Note `path` |
|---|---|---|
| Duplicate `id` in `tasks`, `links`, `resources`, `assignments` or `calendars` | The last record wins and takes the position of the first one | `tasks[i].id`, `links[i].id`, ... |
| Invalid `pauses` on a task | The task's pause list is cleared | `tasks[i].pauses` |
| Dated `constraintType` without `constraintDate` | `constraintType` is set to `null` | `tasks[i].constraint_type` |
| Task with neither `startDate` nor `endDate` | `startDate` is set to [`config.projectStart`](<https://docs.dhtmlx.com/planning-engine/java/api/data/scheduling-config.md#projectstart>), or to the earliest date of any task when `projectStart` is `null` | `tasks[i].start_date` |
| Task (not a milestone) with no `duration` and only one of its dates | `duration` is set to `1.0` | `tasks[i].duration` |
| `startDate`, `endDate` and `duration` that don't agree | The dates win: `duration` becomes the working time between them. When no duration from the start lands on the end (an end in non-working time, or dates computed on other calendars), `duration` is set to `null` and the task is resolved from its dates | `tasks[i].duration` |
| `endDate` inside a work gap of a task with inconsistent dates | The end moves to the start of the gap | `tasks[i].end_date` |
| Summary with only one of its dates | The missing date is computed from the other date and the duration | `tasks[i].start_date` or `tasks[i].end_date` |
| Assignment `offset` that isn't finite or is negative | `offset` is set to `null` | `assignments[i].offset` |
| Assignment `duration` that isn't finite or isn't positive | `duration` is set to `null` | `assignments[i].duration` |

Unscheduled tasks and summaries without dates stay without dates. Repair never falls back to the current date. When an undated task needs a start and the project has neither `config.projectStart` nor any dated task, the call throws [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>) with `path` `tasks[i].start_date`.

## Example

```java
import java.util.List;
import org.dhtmlx.plen.*;

public class RepairInput {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 1, 5, 8, 0);

    ProjectData data = new ProjectData().withTasks(List.of(
        new TaskData(Id.str("design")).withStartDate(monday).withDuration(2.0),
        // Same id as the first task: this record replaces it.
        new TaskData(Id.str("design")).withStartDate(monday).withDuration(3.0),
        // No dates: the start defaults to the earliest task date.
        new TaskData(Id.str("build")).withDuration(2.0)));

    Project project = new Project(new ProjectOptions().withValidation(Validation.REPAIR));
    ScheduleResult result = project.solve(data, new SolveOptions());

    for (Note note : result.notes) {
      if (note instanceof Note.Repaired repaired) {
        System.out.println(repaired.path + ": " + repaired.message);
      }
    }
    // tasks[1].id: duplicate id replaced the earlier record, retaining its position
    // tasks[1].start_date: missing date defaulted to project anchor
  }
}
```

## Related

- [`ProjectOptions`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project-options.md>) - where the mode is set
- [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>) - what strict validation throws
- [`Note`](<https://docs.dhtmlx.com/planning-engine/java/api/results/note.md>) - the repair notes in a result
- [Accept messy input](<https://docs.dhtmlx.com/planning-engine/java/how-to/accept-messy-input.md>) - what repair changes and doesn't fix
- [Validation rules](<https://docs.dhtmlx.com/planning-engine/java/scheduling/project-data.md#validation-rules>) - the input that strict validation rejects
