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


# RejectedOperation

One operation of a change set that [`Project#apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>) didn't apply: where it sits in the change set, and why it was rejected. Instances come from [`ApplyReport.rejected`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md#rejected>), and from [`CandidateResult.error`](<https://docs.dhtmlx.com/planning-engine/java/api/evaluate/candidate-result.md>) when [`Project#evaluate`](<https://docs.dhtmlx.com/planning-engine/java/api/project-evaluate.md>) can't score a candidate.

## Declaration

```java
package org.dhtmlx.plen;

public class RejectedOperation extends ValueRecord implements DeepCloneable<RejectedOperation>
```

## Constructors

### RejectedOperation()

```java
public RejectedOperation()
```

Creates an instance with `path` and `message` set to `null`. The library fills both fields on every instance it returns.

## Fields

| Field | Type | Default | Description |
|---|---|---|---|
| `path` | `String` | `null` | Position of the operation in the change set, as `{group}.{verb}[{index}]`. |
| `message` | `String` | `null` | Why the operation was rejected. |

### path

```java
public String path = null;
```

`group` is `tasks`, `links`, `resources` or `assignments`. `verb` is `add`, `update` or `remove`. `index` is the zero-based position in that list of [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/scheduling-changes.md>). For example, `links.add[0]` is the first entry of `changes.links.add`. [`AppliedOperation.path`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md#appliedoperation>) uses the same form, so every operation of a change set shows up exactly once, in either `applied` or `rejected`.

In [`CandidateResult.error`](<https://docs.dhtmlx.com/planning-engine/java/api/evaluate/candidate-result.md>), a failure while solving or leveling the candidate carries the input path of that error instead, such as a field path. When that error has no path, `path` is an empty string.

### message

```java
public String message = null;
```

The validation message, without the path, e.g. `duplicate link id` or `lag must be finite`. Each change-set type lists the messages its operations can produce; see [`LinkAdd`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/link-add.md#errors>) for an example. The text is for logs and diagnostics, not for matching in code.

## Methods

### withPath, withMessage

```java
public RejectedOperation withPath(String value)
public RejectedOperation withMessage(String value)
```

Each setter assigns its field and returns this instance.

**Returns**: this `RejectedOperation`.

### deepClone

```java
public RejectedOperation deepClone()
```

Returns a new instance with the same `path` and `message`.

**Returns**: a new `RejectedOperation`.

### copy

```java
public RejectedOperation copy()
```

Returns a shallow copy. Both fields are strings, so it's equivalent to `deepClone()`.

**Returns**: a new `RejectedOperation`.

### equals, hashCode, toString

```java
public boolean equals(Object other)
public int hashCode()
public String toString()
```

Inherited from [`ValueRecord`](<https://docs.dhtmlx.com/planning-engine/java/api/support/value-record.md>). Two instances are equal when `path` and `message` are equal. `toString()` prints both fields, e.g. `RejectedOperation{path=links.add[0], message=duplicate link id}`, for logging.

## Example

Add a link whose id already exists, and read the rejection:

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

public class RejectedOperationExample {
  public static void main(String[] args) throws Exception {
    Moment start = Moment.fromYmdHm(2026, 4, 6, 8, 0);

    ProjectData data = new ProjectData()
        .withTasks(List.of(
            new TaskData(Id.num(1)).withStartDate(start).withDuration(2.0),
            new TaskData(Id.num(2)).withStartDate(start).withDuration(3.0)))
        .withLinks(List.of(
            new LinkData().withId(Id.str("L1")).withSource(Id.num(1)).withTarget(Id.num(2))));

    Project project = new Project();
    project.solve(data, new SolveOptions());

    SchedulingChanges changes = new SchedulingChanges();
    changes.links.add.add(new LinkAdd()
        .withId(Id.str("L1"))
        .withSource(Id.num(2))
        .withTarget(Id.num(1)));

    ApplyReport report = project.apply(changes);
    for (RejectedOperation rejected : report.rejected) {
      System.out.println(rejected.path + ": " + rejected.message);
    }
  }
}
```

Output:

```
links.add[0]: duplicate link id
```

A rejection sets [`ApplyReport.needsResync`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md#needsresync>). To continue, see [Recover from rejected changes](<https://docs.dhtmlx.com/planning-engine/java/how-to/recover-from-rejected-changes.md>).

## Related

- [`ApplyReport`](<https://docs.dhtmlx.com/planning-engine/java/api/changes/apply-report.md>) - the report that lists rejections
- [`CandidateResult`](<https://docs.dhtmlx.com/planning-engine/java/api/evaluate/candidate-result.md>) - the other place a `RejectedOperation` appears
- [`Project#apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>) - what a rejection does to the project
- [Recover from rejected changes](<https://docs.dhtmlx.com/planning-engine/java/how-to/recover-from-rejected-changes.md>) - resyncing after a rejection
