Skip to content

fix(vault-sync): deleted-citation error escapes as a traceback, not a clean VaultSyncError #547

Description

@boskodev790

sync_vault's forward-pass handler catches ArtifactNotFoundError to surface a deleted-citation conflict as a clean VaultSyncError — but that exception can never reach it, so the handler is dead and the real error escapes as an uncaught traceback.

wheresrc/vouch/vault_sync.py:467:

try:
    r = vault_to_kb(store, vault_dir, actor=actor)
except ArtifactNotFoundError as e:
    # A vault edit referenced a claim/entity/source that no longer
    # exists in the KB. ... surface it cleanly.
    raise VaultSyncError(f"vault edit references unknown artifact: {e}") from e

vault_to_kb files the edit via propose_page (vault_sync.py:389). propose_page validates each cited id and converts the lookup miss to ProposalError, not ArtifactNotFoundError (proposals.py:299-300):

except ArtifactNotFoundError as e:
    raise ProposalError(f"unknown claim id: {cid}") from e

The two are disjoint hierarchies — ProposalError(RuntimeError) (proposals.py:36) vs ArtifactNotFoundError(KeyError) (storage.py:74) — so except ArtifactNotFoundError never fires and the ProposalError propagates uncaught.

user-visible effect — I drove vouch sync on a page whose cited claim was deleted:

  • today: exit 1 with a raw Python traceback ending vouch.proposals.ProposalError: unknown claim id: c1. The CLI has a dedicated except VaultSyncError renderer (cli.py:5021) that turns domain errors into a one-line Error: ..., but it's bypassed because the real exception isn't a VaultSyncError.
  • expected (and after the fix): Error: vault edit references unknown artifact: unknown claim id: c1.

repro: mirror a page to a vault, edit its body, delete the claim it cites, run vouch sync --direction forward.

scope note: this does not keep watch_vault alive — that loop catches only KeyboardInterrupt, so it ends on either exception. The fix is specifically about surfacing the documented VaultSyncError (which the CLI renders cleanly) instead of an uncaught ProposalError traceback, and about making the dead handler live.

fix: catch ProposalError alongside ArtifactNotFoundError. happy to send it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions