Skip to main content

EngineError

Enum EngineError 

Source
pub enum EngineError {
Show 67 variants DuplicateMem(String), UnknownMem(String), MemQuarantined { mem: String, reason_code: String, reason_message: String, }, ReadOnlyMount(String), CheckNotRecorded { reason: String, }, UnknownType { name: String, schema_ref: String, declared: Vec<String>, suggestion: Option<String>, }, InvalidTitle(SlugError), AlreadyExists { id: String, existing_title: String, existing_is_stub: bool, }, ConstraintUnsatisfied { entity_type: String, entity_id: String, violations: Vec<UnsatisfiedConstraint>, }, SectionFormatRefused { entity_type: String, entity_id: String, violation: SectionFormatViolation, }, RequiredOutgoingUnsatisfied { entity_type: String, entity_id: String, missing: Vec<MissingRequiredOutgoingBlock>, }, NotFound { id: String, }, HashMismatch { id: String, current: String, is_stub: bool, }, HasIncomingRefs { id: String, referrers: Vec<ReferrerInfo>, }, MemHasIncomingRefs { mem: String, referrers: Vec<ReferrerInfo>, }, CrossMemLinkNotAllowed { from_mem: String, to_mem: String, }, CrossMemTargetNotFound { target_id: String, target_mem: String, }, CrossMemEdgeNotDeclared { source_schema: String, target_schema: String, rel_type: String, from_id: String, to_id: String, }, RepairNotNeeded { id: String, recovery: String, }, RenameNoOp { id: String, new_title: String, }, EmptyUpdate { id: String, }, RenameBlockedByCrossMemPolicy { from_mem: String, blocked_referrers: Vec<BlockedReferrer>, }, WikiLinkWithoutRelation { from_id: String, missing: Vec<MissingWikiLink>, }, RelationHasBodyLinks { from_id: String, to_id: String, rel_type: String, body_links: Vec<String>, }, RenamePartialFailure { committed_mems: Vec<String>, failed_mem: String, failure_cause: String, }, StubCannotRelate { id: String, }, StubNotUpdatable { id: String, }, StubNotRenamable { id: String, }, InvalidEntityId { id: String, reason: String, }, InvalidWikiLinkTarget { raw: String, suggested: Option<String>, section: String, link_source: String, reason: String, }, InvalidWikiLinkMem { raw: String, section: String, reason: String, }, ConflictingSectionModes { section: String, modes: Vec<String>, }, RelationshipCycle { rel_type: String, from: EntityId, to: EntityId, existing_path: Vec<EntityId>, path_truncated: bool, }, SetAndUnsetConflict { keys: Vec<String>, }, RequiredFieldUnset { field: String, entity_type: String, field_description: Option<String>, enum_values: Vec<String>, type_write_rules: Vec<String>, on_create: bool, missing: Vec<MissingRequiredField>, }, MissingRequiredSection { entity_type: String, missing_count: usize, sections: Vec<MissingRequiredSection>, type_guidance: BTreeMap<String, Vec<String>>, }, PatchSectionEmpty { section: String, }, PatchOldNotFound { section: String, current_content: String, truncated: bool, }, Validation(ValidationError), ParseAfterWrite(String), Parse(ParseError), Backend(BackendError), SchemaNotFound { mem: String, pin: String, sources: Vec<SchemaSourceDiagnostic>, install_hint: Option<String>, }, EmbeddedSchemaInvalid { mem: String, pin: String, reason: String, }, SchemaPackageInvalid { name: String, version: String, message: String, }, SchemaResolverInit(String), Mem(String), MemNameCollision { name: String, source_origin: String, }, InvalidInput(String), UnknownRemote(String), LocalDivergence { mem: String, remote_ref: String, }, NonFastForward { mem: String, remote: String, }, LocalInvalidState { mem: String, remote: String, detail: String, }, SchemaViolationInFetch { mem: String, ref_name: String, violations: Vec<String>, }, PushedCommitsProtected { mem: String, target_sha: String, pushed_shas: Vec<String>, }, BranchResetHeadMoved { mem: String, expected: String, current: String, }, UnknownRef(String), RenameSimilarityOutOfRange { requested: f32, allowed_min: f32, allowed_max: f32, }, InvalidChangesCursor { mem: String, since: String, }, ReviewMarkNotSet { mem: String, }, MemConfigIncomplete { mem: String, missing_fields: Vec<String>, }, MissingRequiredDescription { rel_type: String, from_id: String, to_id: String, }, DescriptionNotPermitted { rel_type: String, from_id: String, to_id: String, }, RelationManualAuthoringForbidden { rel_type: String, from_id: String, to_id: String, guidance: String, }, SearchUnavailable, MarkdownExportUnsupportedBackend { mem: String, active_backend: String, supported_backends: Vec<String>, }, InvalidAnchor(AnchorValidationError),
}
Expand description

Errors surfaced by [Engine].

Backend lifts BackendError verbatim through a #[from] conversion so the engine layer’s error envelope preserves the backend’s typed Sealed / HashMismatch payloads. The MCP layer branches on the discriminant when mapping into the typed code field of its error envelope.

Variants§

§

DuplicateMem(String)

Engine::from_mounts received two mounts naming the same mem. Configuration error: the persistence adapter or caller produced a malformed mount list.

§

UnknownMem(String)

No mount in this engine names the requested mem. Surfaced before reaching any backend so callers can distinguish “wrong mem name” from “backend failure”.

§

MemQuarantined

The mem exists in the workspace but failed its mem-level boot step and is quarantined — it serves nothing until repaired (degrade, never disappear; quarantine is not tolerance). reason_message is the underlying typed failure verbatim, its final clause the repair command; after the repair, memstead_reload re-attaches the mem without a restart.

Fields

§reason_code: String
§reason_message: String
§

ReadOnlyMount(String)

Mutation rejected because the mount declares [MountCapability::ReadOnly]. Surfaced before reaching the backend so the typed Sealed payload from the archive backend never triggers — capability gating runs first.

§

CheckNotRecorded

A check could not be persisted — either the engine has no workspace root (in-memory engines have no durable check store) or the ledger append failed. A check the caller believes recorded but was not is worse than a refusal, so recording is never best-effort.

Fields

§reason: String
§

UnknownType

Entity type is not declared in the pinned schema for this mem. Carries the declared types (sorted) and a fuzzy suggestion so the agent can recover without re-reading the schema. schema_ref is the pinned <name>@<version>.

Fields

§name: String
§schema_ref: String
§declared: Vec<String>
§suggestion: Option<String>
§

InvalidTitle(SlugError)

Title slug is empty / invalid.

§

AlreadyExists

Create attempted against an id already present in the store. Names the occupant’s title — distinct titles can derive the same slug, so the id alone does not tell the caller which entity holds it. existing_is_stub marks a stub occupant (reachable via rename; the create path adopts stubs instead of refusing).

Fields

§existing_title: String
§existing_is_stub: bool
§

ConstraintUnsatisfied

Write refused: the entity as written would violate a block-tier declared constraint of its type (severity: block in the schema’s constraints). Warn-tier violations warn instead (WarningHint::ConstraintUnsatisfied) — same evaluation, tier decided by the declaration. violations restates each violated declaration so the caller can repair without re-fetching the schema.

Fields

§entity_type: String
§entity_id: String
§

SectionFormatRefused

Write refused: a section body violates its schema-declared markdown format (content / item_pattern / table on the section, format_severity: block). The code and recovery payload come from the violation itself (SECTION_CONTENT_MISMATCH / SECTION_ITEM_PATTERN_MISMATCH / INVALID_TABLE_COLUMNS, or SECTION_CONTENT_INVALID for a reserved setext heading); the payload echoes the declared example where one exists — for an agent, a conforming example outperforms any grammar string.

Fields

§entity_type: String
§entity_id: String
§

RequiredOutgoingUnsatisfied

Write refused: the entity’s final edge set leaves a block-tier required_outgoing block unsatisfied (severity: block on the block). The default warn tier keeps the long-standing warning behavior; this refusal exists only where a schema explicitly promoted the block. Shares the MISSING_REQUIRED_OUTGOING code and missing payload shape with the warning — one condition, one vocabulary, tier decided by the declaration.

Fields

§entity_type: String
§entity_id: String
§

NotFound

Mutation rejected because the named entity is not in the store. Distinct from UnknownMem: the mem exists, the entity does not.

Fields

§

HashMismatch

Optimistic-locking failure: the caller’s expected_hash does not match the entity’s current content_hash in the store. current is the live hash — pass it as expected_hash after re-reading to retry. is_stub is set when the entity is a stub (no body, no content_hash); the corrective action is to pass expected_hash: "" rather than re-read via memstead_entity. Surfaces on details.is_stub so MCP callers branch on the structured payload instead of parsing the message text — pre-fix the wire emitted (current: ) with an empty paren that misdirected toward hash-recovery for a stub-shaped entity.

Fields

§current: String
§is_stub: bool
§

HasIncomingRefs

Refusal to delete or rename an entity because other entities in Write-Mems still reference it. There is no force flag or escape hatch — the agent removes the offending references (via memstead_relate --remove or memstead_update) before retrying. referrers carries the typed referrer info (source id, rel-type, source mem) so the response payload describes the full surface in one round-trip. ReadOnly-mount referrers are excluded from this list — they are handled by the residual- stub demotion path on the destructive mutation.

Fields

§referrers: Vec<ReferrerInfo>
§

MemHasIncomingRefs

Refusal to delete a mem because entities in other Write-Mems still reference entities inside it. Mirrors entity-level Self::HasIncomingRefs at the mem granularity — the edge-graph axis (F15 / CLI F8). Revoking a workspace-level grant only closes the policy axis; this check closes the actual-edge axis so a mem delete that would orphan cross-mem edges refuses with the typed envelope listing every offending (from_id, rel_type, source_mem) triple. No force flag — the operator must memstead_relate --remove (or memstead_update to drop the section) on each referrer first, then retry. ReadOnly-mount referrers stay out of this list and route through the residual-stub demotion path on the destructive mutation, same posture as the entity-level variant.

Fields

§referrers: Vec<ReferrerInfo>
§

CrossMemLinkNotAllowed

Relate across mems rejected because the workspace’s [cross_mem_links] policy (or the per-create-rule default_cross_links synthesis) does not permit from_mem → to_mem. Agents adjust the policy or pick a same-mem target. The hint points at the workspace [cross_mem_links] section.

Fields

§from_mem: String
§to_mem: String
§

CrossMemTargetNotFound

Any add-shaped cross-mem edge write (memstead_relate, memstead_create.relations[], memstead_update.declare_relations, or a body wiki-link) to a target whose mem is mounted MountCapability::ReadOnly and the target is absent. Auto-stub is unavailable across the engine/ReadOnly-mem boundary (the engine cannot persist a stub in a mem it has no write access to), and a read-only mem never gains the entity later — the target must already exist before the link is written.

Fields

§target_id: String
§target_mem: String
§

CrossMemEdgeNotDeclared

memstead_relate across mems pinning schemas with different names refused because the source schema’s cross_mem_relationships: section declares no entry for the target schema’s domain. Each source schema must explicitly enumerate outbound cross-mem edges per target domain; the absence here means the source schema does not speak the target domain’s vocabulary. Eligibility is name-based — a declaration covers every version of the named target schema. The agent’s recovery is to declare the rel-type in the source schema’s cross_mem_relationships: section under the target’s bare schema name (to_schema: <name>).

Orthogonal to the cross_mem_links permission policy: vocabulary and permission fire independently. A policy-admissible edge that violates vocabulary surfaces here; a vocabulary-admissible edge that violates policy surfaces as Self::CrossMemLinkNotAllowed.

Fields

§source_schema: String
§target_schema: String
§rel_type: String
§from_id: String
§to_id: String
§

RepairNotNeeded

memstead_update received repair-shaped input (relations_unset) for an entity that currently passes the conformance check. Repair-powers gate on evidence — a conformance failure on the target entity — and a conformant entity has the focused tools instead: memstead_relate(remove) detaches an edge, the additive memstead_update params evolve content. The entity is not modified.

Fields

§recovery: String
§

RenameNoOp

Rename where the new title would slugify to the existing id. Surfaced as a typed no-op so callers don’t loop on a degenerate retry.

Fields

§new_title: String
§

EmptyUpdate

memstead_update / memstead_batch_update payload parsed cleanly but carries no recognised mutation content — every mutation map is empty and no relations are declared. Distinct from UPDATE_NOOP (a warning that fires when mutation content was provided but matched the current state): EMPTY_UPDATE is keyed on “no mutation content provided at all”, and refuses before any mutation work runs so a misspelled/omitted mutation key doesn’t silently land as succeeded: 1, commit_sha: "".

Fields

§

RenameBlockedByCrossMemPolicy

memstead_rename cannot proceed because one or more cross-mem referrers would emit a propagated rewrite whose direction the workspace’s cross_mem_links policy does not permit. The engine refuses the rename up-front (before any write); the agent’s recovery is either to grant the missing direction in [cross_mem_links] or to drop the offending edges first.

Each blocked_referrers entry names a single blocked direction (from_mem → to_mem) — the referrer’s mem and the renaming entity’s mem, respectively — together with the count of distinct referrers in that mem that would emit the blocked rewrite. The direction is the edge’s actual direction post-rewrite (referrer → renamed), which is what the policy gates.

Fields

§from_mem: String
§blocked_referrers: Vec<BlockedReferrer>
§

WikiLinkWithoutRelation

memstead_create / memstead_update / memstead_batch_update refused because the post-mutation entity’s section bodies contain inline wiki-links to targets that have no corresponding explicit relation in entity.relationships. Strict wiki-link / relation invariant: every body wiki-link must have a backing relation. The agent’s recovery is memstead_relate <this-entity> REFERENCES <target> (or a more specific rel-type) for each missing entry, then re-issue the mutation. missing enumerates each violation as a (section_key, target_id) pair so the agent can fix every surviving link in one pass. This validator is gated behind the workspace’s reference-coherence migration completion marker; workspaces that haven’t been migrated continue running the permissive auto-stub regime.

Fields

§from_id: String

memstead_relate --remove refused because the source entity’s section bodies still contain [[<target>]] (or [[<mem>:<target>]]) wiki-links pointing at the relation’s target. Removing the explicit relation while body links survive would violate the strict wiki-link/relation invariant (inline links require a backing relation). The agent’s recovery is memstead_update <source-id> with section content that drops the wiki-link tokens, then re-issue memstead_relate --remove. body_links enumerates the surviving section keys so the agent can patch them in one pass.

Fields

§from_id: String
§to_id: String
§rel_type: String
§body_links: Vec<String>
§

RenamePartialFailure

A multi-mem memstead_rename partially landed: at least one mem committed successfully, then a subsequent per-mem commit aborted (typically because a sibling writer advanced the failed mem’s head between the rename’s snapshot and the commit attempt — the parent-ref pin tripped via BackendError::ParentMismatch). The committed mems’ state has already landed and is durable; the failed mem’s writes did not land. The agent’s recovery options: retry the rename (reload the workspace first so the engine re-derives the right referrer set), or accept the partial state and reconcile manually via subsequent mutations.

Fields

§committed_mems: Vec<String>
§failed_mem: String
§failure_cause: String
§

StubCannotRelate

memstead_relate source is a stub — stubs have no entity_type and cannot author edges. The agent must promote the stub to a real entity via memstead_create (stub adoption preserves any incoming references) before relating. Pre-fix surfaced as the cryptic UnknownType { name: "" }.

Fields

§

StubNotUpdatable

memstead_update target is a stub — stubs have no body, no metadata, no schema-resolved type to validate against. The agent must promote the stub to a real entity via memstead_create (stub adoption preserves any incoming references) before updating. Pre-Item-02 surfaced as the cryptic UnknownType { name: "" } cascade — identical symptom to the one StubCannotRelate was added to replace on memstead_relate.

Fields

§

StubNotRenamable

memstead_rename target is a stub — stubs do not have a title to rename (their title is derived from the id). Same recovery path as Self::StubNotUpdatable.

Fields

§

InvalidEntityId

An EntityId reaching a write path (notably memstead_relate to=) does not match the wiki-link grammar (^[a-z0-9-]+(/[a-z0-9-]+)*$ for the slug; ^[a-z0-9-]+$ for the mem). The gate prevents an auto-stub being created at a malformed id — once present, that stub would fail any downstream wiki-link parse that referenced it.

Fields

§reason: String
§

InvalidWikiLinkTarget

A body wiki-link target in a section body failed the strict slug-form grammar gate. The invariant is that every wiki-link target reaching entity.relationships carries a grammar-valid EntityId — the alias-synthesis pass would otherwise emit a relation pointing at a literal id (e.g. mem--Knowledge Graph) that no downstream wiki-link parse could ever resolve. raw is the input between brackets (after alias / .md strip); suggested is the title_to_slug-derived slug-form the agent lifts directly into the retry (omitted when the input has no meaningful canonical form — empty, all-punctuation, all-emoji); section is the section key whose body carried the link; source is a stable discriminator ("body_link") future- proofed against additional ingress surfaces.

Fields

§suggested: Option<String>
§section: String
§link_source: String
§reason: String
§

InvalidWikiLinkMem

A body wiki-link’s Tier-2 mem prefix [[mem:slug]] failed the mem-name grammar (^[a-z0-9-]+(/[a-z0-9-]+)*$). Distinct from InvalidWikiLinkTarget because the recovery is different — mem names are fixed identifiers in the workspace, not free-form text the agent can mechanically slugify; the agent correlates the bad prefix against the workspace’s known mems rather than reaching for title_to_slug.

Fields

§section: String
§reason: String
§

ConflictingSectionModes

memstead_update was asked to apply more than one section- mutation mode (sections, append_sections, patch_sections) to the same key. The request is ambiguous and rejected before any disk write. modes lists the conflicting modes for the key in canonical order.

Fields

§section: String
§modes: Vec<String>
§

RelationshipCycle

Adding the proposed edge would close a cycle in an acyclic-declared subgraph. Carries the existing back-path [from, …, current, target's intermediates, … from] so MCP envelopes ship the cycle’s shape without a follow-up memstead_search. Truncated at [RELATIONSHIP_CYCLE_PATH_CAP] entries.

Fields

§rel_type: String
§existing_path: Vec<EntityId>
§path_truncated: bool
§

SetAndUnsetConflict

memstead_update received the same metadata key in both metadata (set) and metadata_unset lists. The request is ambiguous and rejected before any disk write — the caller picks which map the key belongs in. keys lists every overlapping key in alphabetical order so a single envelope describes the full conflict.

Fields

§keys: Vec<String>
§

RequiredFieldUnset

metadata_unset targeted a required field. Carries the recovery payload so the agent reads the field’s purpose, allowed values, and type-level write rules from the envelope rather than re-fetching the schema.

Also fires from memstead_create when the caller did not supply a required metadata field that the schema does not auto-fill (default_value / init_timestamp / auto_timestamp all absent). Pre-fix the create path surfaced this as a MISSING_REQUIRED_FIELD warning and let the entity land with a placeholder — silently corrupted the export-then-install round-trip when the placeholder was invalid for the install-time strict validator. The refusal fires once per call on the first missing field (declaration order); subsequent fields surface on the next attempt.

Fields

§field: String
§entity_type: String
§field_description: Option<String>

Schema-supplied description of the field.

§enum_values: Vec<String>

Allowed enum values when the unset field is enum-typed; empty when the field is free-form.

§type_write_rules: Vec<String>

Type-level write_rules for the entity type.

§on_create: bool

Path discriminator: true when the create path constructed the variant (caller didn’t supply the field), false when the update path constructed it (caller passed metadata_unset: ["field"] against a required field). The typed code stays REQUIRED_FIELD_UNSET on both paths; only the rendered prose differs.

Not exposed on the details payload — agents already branch on the typed code; the new field is for the prose dispatch only.

§missing: Vec<MissingRequiredField>

Multi-field accumulator on the create path. Every required-no-default field that was unset, in schema declaration order. Empty on the unset path (where the agent targets one field by definition and the singular fields above are authoritative); always non-empty (and at least a singleton echo of the singular fields) on the create path.

Surfaces on details.missing[] so an agent fixes every missing field in one round-trip. details.field and details.missing[0].field agree on the first-missing entry, keeping the back-compat singular-field shape.

§

MissingRequiredSection

memstead_create: one or more required sections for the entity’s type were absent or whitespace-only in the request. Pre-fix the create path surfaced this as MISSING_REQUIRED_SECTION warnings and wrote the entity with empty placeholders for the missing sections; the resulting on-disk state failed the install-time strict validator, breaking the export-then- install round-trip. The refusal carries every missing section (one entry per affected key) plus the type-level type_guidance map so the agent has a single round-trip recovery via re-call with the missing content filled in.

Loader / health / memstead_update paths keep their permissive posture — a legacy on-disk entity created when this gate was a warning continues to load, surface in health, and accept partial updates. The refusal is a write-boundary gate, not a global invariant.

Fields

§entity_type: String
§missing_count: usize

Echoed for diagnostics; equals sections.len().

§sections: Vec<MissingRequiredSection>

One entry per missing required section, in schema declaration order. Each entry mirrors the shape of the pre-fix WarningHint::MissingRequiredSection warning so agents reading the recovery payload don’t branch on surface (refusal vs warning).

§type_guidance: BTreeMap<String, Vec<String>>

Type-level write_rules keyed by entity_type. Map shape matches the mutation-response top-level type_guidance the warning-surface ships so a single decoder reads guidance from either path.

§

PatchSectionEmpty

patch_sections targeted a key whose section body is absent from the entity (or has never been authored).

Fields

§section: String
§

PatchOldNotFound

patch_sections provided an old substring that does not appear in the section’s current body. Carries a truncated snapshot of the current content so the caller can surface the actual state to the operator.

Fields

§section: String
§current_content: String
§truncated: bool
§

Validation(ValidationError)

Schema-strictness rejection from the runtime validator (UNKNOWN_SECTION, UNKNOWN_METADATA, INVALID_ENUM_VALUE).

§

ParseAfterWrite(String)

Re-parse of the freshly-generated markdown failed. Should never happen — the generator’s contract is that its output round-trips through parse_markdown. Surfaces if a future generator change breaks that invariant.

§

Parse(ParseError)

A wrapped parse error for completeness; today only the parse-after-write variant above is constructed in the create path.

§

Backend(BackendError)

A backend operation failed. Inner error carries the typed payload (e.g. Sealed, HashMismatch, Io).

§

SchemaNotFound

A mem’s schema pin did not resolve. sources carries the fixed-order resolution diagnostics (local storage / built-in / remote) so the caller can tell where the pin failed and spot a right-name/wrong-version partial match; it surfaces under details.sources. Empty sources marks an internal lookup miss (an already-resolved schema absent from the engine’s per-mem map), not a genuine source-resolution failure.

The MESSAGE summarises the trail — which sources were searched and whether the name was found at other versions — so the distinction between a wrong-version pin and a never-installed package reaches consumers that never open details (a reported autonomous loop burned five rounds on the payload-only shape). install_hint (set by EngineError::with_schema_install_probe where a workspace root is known) names the authoring package that exists in the working tree but was never installed, and the message then points at memstead schema install.

Fields

§install_hint: Option<String>

Path to an authoring package in the working tree whose manifest name matches the pin’s name while NO source holds any version of that name — i.e. the package was authored but never installed. None when no such package exists, when the name is installed at other versions (a version mismatch is a different fix), or when no workspace root was available to probe.

§

EmbeddedSchemaInvalid

A sealed schema package carried inside a mem archive could not be loaded — the archive’s own .memstead/schema/ tree is broken. Deliberately NOT SchemaNotFound: the package is right here, so the recovery is never “obtain the schema and install it”. The message quotes the loader’s own diagnosis and the refusal leaves nothing mounted and nothing staged; only the publisher can fix it.

Fields

§reason: String
§

SchemaPackageInvalid

A schema package handed to install_schema failed validation — the loader’s semantic checks or the section-heading round-trip gate. The engine refuses to seal an invalid schema onto __MEMSTEAD: install time is the last moment the author can act, because a schema already sealed keeps loading even when a later rule would refuse it.

Fields

§name: String
§version: String
§message: String
§

SchemaResolverInit(String)

memstead_schema::builtins::load_builtin_schemas itself failed. Surfaces during Engine::from_mounts; should never trip in practice (the built-in catalogue is statically embedded), but the failure path is preserved so a future on-disk catalogue switch lifts cleanly.

§

Mem(String)

Generic mem-level error message — used by accessors that surface “mem exists, but the requested resource is not available for this backend” (e.g. gitdir_for against a folder mount, worktree_for against a git-branch mount).

§

MemNameCollision

register_writable_mem rejected because name is already registered (writable OR read-only). source_origin is the human-readable description of the colliding registration, rendered via [MemOrigin::render_source] for writable entries or a stand-in for read-only ones.

Fields

§name: String
§source_origin: String
§

InvalidInput(String)

Lifecycle orchestrator rejected the input. Carries a single free-form message — the orchestrator’s typed payload (note length, malformed path, etc.) is the message text.

§

UnknownRemote(String)

memstead_fetch / memstead_pull / memstead_push named a remote that is not configured on the workspace’s mem-repo. Typed code UNKNOWN_REMOTE. Recovery: configure the remote via memstead mem-repo remote-add <name> <url>.

§

LocalDivergence

memstead_pull refused because the local branch has diverged from the remote-tracking ref — fast-forward is impossible without losing local commits. Recovery: run memstead branch-reset to the remote-tracking ref (if the local commits are dispensable) or run a replay workflow to rewrite them onto the new remote tip. Typed code LOCAL_DIVERGENCE.

Fields

§remote_ref: String
§

NonFastForward

memstead_push refused because the push would not be a fast-forward against the remote and the caller did not pass force: true. Typed code NON_FAST_FORWARD. Recovery: re-fetch + replay, or re-issue with force: true (warning: rewrites the remote’s view of the branch — other peers will see the rewrite).

Fields

§remote: String
§

LocalInvalidState

memstead_push refused because the local state failed pre-push schema validation. The remote was not contacted. Recovery: fix the schema violations (use memstead_health to find them) and retry. Typed code LOCAL_INVALID_STATE.

Fields

§remote: String
§detail: String
§

SchemaViolationInFetch

memstead_pull (or any future merge path that consumes fetched commits) refused because the prospective post-merge tree contains entities that fail schema validation. The branch pointer was not moved. violations carries one entry per offending entity — typically (relative_path, parse_error) pairs rendered as strings — so the caller can surface the remediation surface without re-walking the tree. Typed code SCHEMA_VIOLATION_IN_FETCH.

Fields

§ref_name: String
§violations: Vec<String>
§

PushedCommitsProtected

memstead_branch_reset refused because at least one commit that would be discarded by the reset is already reachable from a refs/remotes/* ref (the engine’s definition of “pushed”). pushed_shas lists the offending commits. The agent’s recovery is to pick a target SHA that does not strand a pushed commit, or to push the pre-reset state under a different branch name first. Typed code: PUSHED_COMMITS_PROTECTED.

Fields

§target_sha: String
§pushed_shas: Vec<String>
§

BranchResetHeadMoved

branch_reset refused because the live branch head no longer matches the head the caller observed (expected_head) — a sibling writer advanced the mem, and resetting now would discard that foreign work. Optimistic concurrency for history rewrites; the caller re-reads and re-decides. Typed code: BRANCH_RESET_HEAD_MOVED.

Fields

§expected: String
§current: String
§

UnknownRef(String)

memstead_diff (or any future ref-comparing op) received a ref that does not resolve against the workspace’s mem-repo. Carries the ref string verbatim so the caller can fix the input. Typed code UNKNOWN_REF.

§

RenameSimilarityOutOfRange

memstead_changes_since received a rename_similarity value outside the allowed range. Maps to wire code INVALID_INPUT with details.allowed_range: [min, max] and details.requested. Promoted from the prior silent-clamp + LIMIT_CLAMPED warning so nonsense inputs surface as recoverable refusal rather than silent rounding.

Fields

§requested: f32
§allowed_min: f32
§allowed_max: f32
§

InvalidChangesCursor

memstead_changes_since / memstead changes --since was given a since commit cursor the mem’s git repository can’t resolve — a malformed prefix or a well-formed-but-absent 40-hex. Surfaces the INVALID_CURSOR code (the documented contract for this op, which the CLI previously leaked as the MEM_ERROR catch-all) so a sync loop branches cleanly: INVALID_CURSOR → re-seed from the empty-tree sentinel; MEM_ERROR → genuine backend fault. details.since carries the offending cursor untruncated.

Fields

§since: String
§

ReviewMarkNotSet

review_mark_diff was called on a mem with no review mark set. Marklessness is a first-class, known-from-the-roster state — the diff surface refuses typed rather than silently equating “no mark” with “no changes”.

Fields

§

MemConfigIncomplete

Mem config is missing a required field that the engine itself would normally populate (today: version at mem init). Surfaced on the export path — pre-fix this collapsed to INTERNAL with a misleading .memstead/config.json reference that doesn’t match the mem-repo backend’s blob layout. Recovery: run memstead mem set-version <mem> <version> to populate the field, then retry the export. F1.

Fields

§missing_fields: Vec<String>
§

MissingRequiredDescription

memstead_relate (or a declare_relations entry) targeted a rel-type whose schema declares per_edge_description: required without supplying a description. Recovery: re-issue the call with --description "<text>" describing why this particular edge exists (the rel-type’s name documents the kind of edge; the description documents the instance).

Fields

§rel_type: String
§from_id: String
§to_id: String
§

DescriptionNotPermitted

memstead_relate (or a declare_relations entry) supplied a description for a rel-type whose schema declares per_edge_description: forbidden. Recovery: drop the description parameter — the rel-type’s name describes the edge; per-edge text is not permitted on this rel-type.

Fields

§rel_type: String
§from_id: String
§to_id: String
§

RelationManualAuthoringForbidden

memstead_relate (or a declare_relations / memstead_create’s inline relations: entry) targeted a rel-type whose schema declares manual_authoring: forbidden. The rel-type is reserved for engine-emitted synthesis (the body-link → relation alias machinery, typically). Recovery: don’t author the relation explicitly; instead author a body wiki-link [[target]] in the source’s section content, which the engine surfaces as the appropriate alias relation automatically.

Fields

§rel_type: String
§from_id: String
§to_id: String
§guidance: String
§

SearchUnavailable

Full-text search is unavailable in the current engine build — Engine::search is callable on every target so JS / FFI consumers don’t need to re-shape their call sites, but wasm32 builds omit the tantivy index entirely (its native-only transitives — getrandom 0.2 without js, memmap2, rayon, zstd-sys — block WASM compilation). Browser consumers route queries to the bridge’s memstead_search endpoint. The MCP layer maps this to typed code SEARCH_UNAVAILABLE_IN_WASM.

§

MarkdownExportUnsupportedBackend

memstead export --format markdown --mem-name <V> was called against a mem whose active backend doesn’t support markdown regeneration in place (today: every backend other than folder). Pre-fix this collapsed to a silent ExportResult { written: 0, unchanged: 0 } masquerading as success. Recovery: use --format mem to produce a portable .mem archive, which every backend supports.

Fields

§active_backend: String
§supported_backends: Vec<String>
§

InvalidAnchor(AnchorValidationError)

A memstead_create / memstead_update anchors[] element was malformed — an unknown provenance class or grain, a missing artifact reference, a content hash on a class without hash semantics, or a grain the resolving medium’s namespace cannot express. The whole mutation refuses and the entity is not written; the wrapped crate::anchor::AnchorValidationError carries the recovery details (offending field, bad value, allowed set). Typed code INVALID_ANCHOR.

Implementations§

Source§

impl EngineError

Source

pub fn code(&self) -> &'static str

Stable, surface-independent error code token.

Each surface (MCP envelope, CLI envelope, UniFFI binding) maps the variant to its wire shape; the code returned here is the canonical name agents key on. Add a new code here when a new variant lands; do not invent ad-hoc strings inside the per-surface mapping.

Source

pub fn details(&self) -> Value

Variant-specific recovery payload, rendered as a structured JSON object that surfaces under error.details in MCP / CLI envelopes.

Pre-fix the batch-update per-item envelope (batch_error_envelope) shipped {} for every typed code except Validation, while the singleton-call surfaces (CliError::from_engine_op, memstead-mcp‘s engine_err_unified) populated structured payloads per-variant. Two envelopes, two details paths — agents’ “fix from details” recovery loop worked differently in batch vs singleton mode. The centralised helper here gives both surfaces one source of truth.

Returns an empty object for variants whose recovery payload is the message text alone (no structured fields beyond code + message).

Source

pub fn prose_render(&self) -> String

Render rich, fully-inlined recovery prose for the agent-visible text channel.

Warnings already render their structured payload inline via WarningHint::Display; pre-fix errors with rich payloads collapsed to Display plus format_inline_list_overflow’s “+N more — see details.X” pointer pointing at a structured channel the agent’s MCP client doesn’t surface to the model. This method gives errors the same prose-rich rendering warnings have, so result.content[0].text is self-recoverable.

Variants whose Display already inlines every recovery field (no truncation, no “see details” pointer) inherit the default trait impl — they just to_string(). Override only the variants that need richer rendering than Display provides.

The structured details() channel is unchanged; consumers branching on code continue to receive the typed shape. The Display impl stays terse for logs, tracing, panic messages, and other non-agent consumers.

Source§

impl EngineError

Source

pub fn with_schema_install_probe(self, workspace_root: Option<&Path>) -> Self

Attach the schema-install hint to a SchemaNotFound where a workspace root is known: probe the root’s immediate subdirectories for an authoring schema package (a directory the schema loader accepts) whose manifest name matches the pin’s name, and record its path when NO resolution source holds any version of that name. Any other error variant — and any SchemaNotFound where the name IS installed at some version (a version mismatch is a different fix), where sources is empty (internal miss, no source search happened), or where no candidate package exists — passes through unchanged. Read-only: the probe never writes, installs, or seals anything.

Trait Implementations§

Source§

impl Debug for EngineError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for EngineError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for EngineError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<AnchorValidationError> for EngineError

Source§

fn from(source: AnchorValidationError) -> Self

Converts to this type from the input type.
Source§

impl From<BackendError> for EngineError

Source§

fn from(source: BackendError) -> Self

Converts to this type from the input type.
Source§

impl From<EngineError> for FromArchiveBytesError

Source§

fn from(source: EngineError) -> Self

Converts to this type from the input type.
Source§

impl From<EngineError> for BootError

Source§

fn from(source: EngineError) -> Self

Converts to this type from the input type.
Source§

impl From<ParseError> for EngineError

Source§

fn from(source: ParseError) -> Self

Converts to this type from the input type.
Source§

impl From<SlugError> for EngineError

Source§

fn from(source: SlugError) -> Self

Converts to this type from the input type.
Source§

impl From<ValidationError> for EngineError

Source§

fn from(source: ValidationError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Fruit for T
where T: Send + Downcast,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more