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


# RejectedOperation

One operation of a change set that the engine refused. [`Project::apply`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#apply>) lists these in [`ApplyReport::rejected`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/apply-report.md#rejected>), and [`CandidateResult::error`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/candidate-result.md>) uses the same type for a candidate that failed. It's read-only output: the engine creates it, the caller only reads the fields.

## Declaration

```rust
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RejectedOperation {
    pub path: String,
    pub message: String,
}
```

With the `serde` feature, the type also implements `Serialize` and `Deserialize`, with camelCase field names.

## Fields

| Field | Type | Description |
|---|---|---|
| `path` | `String` | Where the operation sits in the change set, as `group.verb[index]`, for example `tasks.update[0]` or `links.add[2]`. The index counts within that list of [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/scheduling-changes.md>). In [`CandidateResult::error`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/candidate-result.md>), a failure that comes from scheduling or leveling the candidate carries the path of the input error, or an empty string when that error has no path. |
| `message` | `String` | Why the operation was refused, for example `unknown task id`. Plain text for display or logging, not a stable error code. |

## Example

Update a task that doesn't exist and read the rejection.

```rust
use plen_core::{
    DateTime, Patch, Project, ProjectData, ProjectOptions, SchedulingChanges, SolveOptions,
    TaskData, TaskUpdate,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 4, 6, 8, 0).ok_or("invalid date")?;

    let data = ProjectData {
        tasks: vec![TaskData {
            start_date: Some(monday),
            duration: Some(2.0),
            ..TaskData::new(1)
        }],
        ..Default::default()
    };

    let mut project = Project::new(ProjectOptions::default());
    project.solve(Some(data), &SolveOptions::default())?;

    let mut update = TaskUpdate::new(99);
    update.duration = Patch::Value(3.0);

    let mut changes = SchedulingChanges::default();
    changes.tasks.update.push(update);

    let report = project.apply(&changes)?;
    for rejected in &report.rejected {
        // tasks.update[0]: unknown task id
        println!("{}: {}", rejected.path, rejected.message);
    }
    Ok(())
}
```

## Related

- [`ApplyReport`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/apply-report.md>) - lists rejected operations in `rejected`
- [`AppliedOperation`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/changes/applied-operation.md>) - the counterpart for accepted operations
- [`CandidateResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/candidate-result.md>) - reports a failed candidate with this type
- [Recover from rejected changes](<https://docs.dhtmlx.com/planning-engine/rust/how-to/recover-from-rejected-changes.md>) - shows what to do after a rejection
- [Error handling](<https://docs.dhtmlx.com/planning-engine/rust/error-handling.md>) - explains why rejections aren't errors
