[Canonical page](<https://docs.dhtmlx.com/planning-engine/wasm/how-to/accept-messy-input/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Accept messy input

Schedule project data that strict validation rejects, such as data exported from a UI with duplicate ids or missing dates, and get a list of every fix the engine made.

By default a [`JsonProject`](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md>) uses [`"validation": "strict"`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#validation-mode>) and returns a `validation` [error envelope](<https://docs.dhtmlx.com/planning-engine/wasm/wire-format.md#envelope>) for the first invalid field. `"validation": "repair"` coerces invalid data instead (deduplicates ids, derives missing dates, fixes inconsistent dates), then schedules the result.

## Steps

1. Create the project with repair validation:

   ```js
   const project = new JsonProject(JSON.stringify({ validation: "repair" }));
   ```

   The [constructor](<https://docs.dhtmlx.com/planning-engine/wasm/api/json-project.md#creating>) throws the error envelope as a string if the options are invalid, for example `"validation": "fix"`.

2. Pass your data to [`solve`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-solve.md>) as usual. [`analyze`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-analyze.md>) with a data string repairs supplied data the same way. The one-shot [`schedule`](<https://docs.dhtmlx.com/planning-engine/wasm/api/schedule.md>) export always uses strict validation, so use a `JsonProject` here.

3. Read the entries with `kind: "repaired"` in [`result.notes`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md#note>) and show them to the user, or log them. Each one has a `path` and a `message`:

   ```json
   { "kind": "repaired", "message": "duplicate id replaced the earlier record, retaining its position", "path": "tasks[3].id" }
   ```

The full example below has four kinds of problems: a duplicate id, a task with no dates, a dated constraint with no date, and a task whose start, end and duration disagree. Strict validation rejects this data. Repair schedules it:

```js
const { JsonProject } = require("./pkg-node/plen_wasm.js");

function unwrap(text) {
  const response = JSON.parse(text);
  if (!response.ok) {
    const { kind, code, message, path } = response.error;
    throw new Error(`${kind} ${code ?? ""}: ${message} (at ${path})`);
  }
  return response.result;
}

const data = {
  // 24/7 time keeps the example dates easy to check.
  config: { use_calendars: false },
  tasks: [
    // Start and end without a duration: valid, nothing to repair.
    { id: "design", start_date: "2026-01-01 00:00", end_date: "2026-01-04 00:00" },
    // "build" appears twice. The later record wins.
    { id: "build", start_date: "2026-01-01 00:00", duration: 1 },
    // No dates, no duration, and a dated constraint without `constraint_date`.
    { id: "review", constraint_type: "snet" },
    { id: "build", start_date: "2026-01-01 00:00", duration: 2 },
    // Three days between start and end, but `duration` says five.
    { id: "deploy", start_date: "2026-01-01 00:00", end_date: "2026-01-04 00:00", duration: 5 },
  ],
};

const options = JSON.stringify({ includeUnchanged: true });

const project = new JsonProject(JSON.stringify({ validation: "repair" }));
try {
  const result = unwrap(project.solve(JSON.stringify(data), options));
  for (const note of result.notes) {
    if (note.kind === "repaired") {
      console.log(`${note.path}: ${note.message}`);
    }
  }
  for (const u of result.updates) {
    console.log(`${u.id} ${u.start_date}..${u.end_date} ${u.duration}`);
  }
} finally {
  project.free();
}
```

## What repair changes

Repair reports each change below as one `repaired` note in [`ScheduleResult.notes`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/schedule-result.md#note>), with the field path in `path`:

- Duplicate ids in `tasks`, `links`, `resources`, `assignments` and `calendars`: the last record replaces the earlier one and takes its position. The note is at `tasks[i].id`, where `i` is the index of the duplicate in your input.
- A task with no start and no end gets `start_date` set to [`config.project_start`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/scheduling-config.md>), or to the earliest date of any task if `project_start` isn't set.
- A leaf task (not a milestone) with no `duration` and only one date gets `duration: 1`.
- Start, end and duration that disagree: the dates win, and the duration becomes the working time between them. If the end is in non-working time, or the dates were computed on a different calendar, the duration is dropped and the task is resolved from its dates. An end that falls inside a pause moves to the pause start, with a second note at `tasks[i].end_date`.
- A summary with only one date gets the other date computed from the date and its duration.
- A dated [constraint](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#constrainttype>) (`"snet"`, `"snlt"`, `"fnet"`, `"fnlt"`, `"mso"`, `"mfo"`) without `constraint_date` is cleared.
- Invalid [`pauses`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/common.md#pause>) on a task: the whole list is cleared, with a note at `tasks[i].pauses`.
- An invalid [assignment](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/assignment-data.md>) `offset` or `duration` is cleared, so the assignment books the whole task.

A task that already has two of start, end and duration is valid input. Repair doesn't touch it and doesn't report it.

Only the duplicate-id notes use input indexes. Every other note indexes the deduplicated list. In the example, the two `build` records become one, so `deploy` is `tasks[4]` in your input but `tasks[3]` in its note.

## What repair doesn't fix

- An entirely undated project, with no [`config.project_start`](<https://docs.dhtmlx.com/planning-engine/wasm/api/types/scheduling-config.md>) and no task that has a date, still fails with a `validation` error at `tasks[i].start_date`. The engine never falls back to today's date.
- Every other invalid input, such as a cycle in the `parent` hierarchy or a calendar with no working time, still returns a `validation` error. See [Error handling](<https://docs.dhtmlx.com/planning-engine/wasm/error-handling.md>).
- If repair or validation fails, [`solve`](<https://docs.dhtmlx.com/planning-engine/wasm/api/project-solve.md>) returns an error envelope (`ok: false`) and the project keeps its previous state.

## Result

Run the example with `node`. It prints the fixes, then the scheduled dates:

```text
tasks[3].id: duplicate id replaced the earlier record, retaining its position
tasks[2].constraint_type: dated constraint without date cleared
tasks[2].start_date: missing date defaulted to project anchor
tasks[2].duration: missing duration defaulted to one
tasks[3].duration: duration recomputed from supplied start and end
design 2026-01-01 00:00..2026-01-04 00:00 3
build 2026-01-01 00:00..2026-01-03 00:00 2
review 2026-01-01 00:00..2026-01-02 00:00 1
deploy 2026-01-01 00:00..2026-01-04 00:00 3
```

`build` uses the second record's duration of 2, and `deploy` has a duration of 3, taken from its dates. With `new JsonProject("{}")`, the same `solve` call returns a `validation` error at `tasks[2].constraint_date`.
