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


# ProjectError

The reason a [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) call doesn't fit the project's current state: there's no solved baseline, a `baseline_id` token is out of date, or the project needs a full reload. It reaches you wrapped in [`Error::Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>). The project's state is unchanged when the call fails.

## Declaration

```rust
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProjectError {
    NoBaseline,
    StaleBaseline,
    NeedsResync,
}

impl std::error::Error for ProjectError {}
impl std::fmt::Display for ProjectError {}
```

`ProjectError` is `Copy` and `PartialEq`, so it can be compared with `==`. With the `serde` feature it also implements `Serialize` and `Deserialize`, as the same kebab-case strings that `Display` prints.

## Variants

| Variant | `Display` | When it occurs |
|---|---|---|
| `NoBaseline` | `no-baseline` | The project has no accepted baseline: it was never solved, or it was edited with [`apply`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#apply>) after the last solve. Returned by [`preview`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#preview>), [`load_profile`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#load_profile>), [`evaluate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#evaluate>), [`evaluate_with`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#evaluate_with>), [`suggest_assignments`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#suggest_assignments>) and [`roster`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#roster>). |
| `StaleBaseline` | `stale-baseline` | A `baseline_id` passed to the call doesn't name the current accepted baseline, including when the project is unsolved. Returned by `preview` (options or changes token), `apply` (changes token), `evaluate` and `evaluate_with` (options token or any candidate's changes token), `suggest_assignments` and `load_profile` (options token). |
| `NeedsResync` | `needs-resync` | An earlier `apply` rejected at least one operation, so the stored data may no longer match the caller's. Returned by [`solve(None)`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>), `apply` and [`level`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#level>) until `solve(Some(data))` replaces the data. |

On calls that can return both `NoBaseline` and `StaleBaseline`, the baseline is checked first: an unsolved project gives `NoBaseline` even when a token is also passed.

Take the token from the latest solve result. See [`Project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) for which result carries `baseline_id`.

`MatchError::Project` from [`slots`](<https://docs.dhtmlx.com/planning-engine/rust/api/assign/slots.md>) wraps the same type. See [`MatchError`](<https://docs.dhtmlx.com/planning-engine/rust/api/assign/match-error.md>).

## Example

```rust
use plen_core::{Error, Project, ProjectError, SchedulingChanges};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut project = Project::default();

    // The token doesn't name the current baseline (the project has none).
    let changes = SchedulingChanges {
        baseline_id: Some("outdated-token".to_string()),
        ..Default::default()
    };

    match project.apply(&changes) {
        Ok(report) => println!("{} operations applied", report.applied.len()),
        Err(Error::Project(ProjectError::StaleBaseline)) => println!("re-read the baseline and retry"),
        Err(Error::Project(ProjectError::NeedsResync)) => println!("reload with solve(Some(data))"),
        Err(e) => return Err(e.into()),
    }
    Ok(())
}
```

For recovery patterns, see [Error handling](<https://docs.dhtmlx.com/planning-engine/rust/error-handling.md>).

## Related

- [`Error`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/error.md>) - wraps this type in `Error::Project`
- [`ScheduleResult::baseline_id`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md#baseline_id>) - the token a `StaleBaseline` check compares against
- [Previewing Changes](<https://docs.dhtmlx.com/planning-engine/rust/live-projects/previewing-changes.md>) - shows how to guard calls with baseline tokens
- [Recover from rejected changes](<https://docs.dhtmlx.com/planning-engine/rust/how-to/recover-from-rejected-changes.md>) - shows how to clear `NeedsResync`
