Complete Guide to Transaction Error Codes in Claude‑Obsidian

Claude‑Obsidian defines 23 specific string error codes across four exception classes—TransactionError, TransactionValidationError, TransactionConflict, and TransactionRecoveryError—to identify exact failure points ranging from invalid file paths to unsafe rollback conditions.

The transaction system in AgriciDaniel/claude-obsidian enforces strict safety guarantees when modifying Obsidian vaults. Located in claude_obsidian/transaction.py, the implementation raises granular transaction error codes that allow callers to distinguish between validation failures, runtime conflicts, and recovery errors without parsing ambiguous log messages.

Exception Hierarchy and Exit Codes

All transaction failures in claude-obsidian inherit from a base exception class mapped to specific process exit codes.

TransactionError serves as the abstract base for all transaction-related failures and returns exit code 1.

TransactionValidationError signals pre-condition violations before any vault modification occurs and returns exit code 2. This covers path validation, identity checks, and platform compatibility.

TransactionConflict indicates that a file changed while being inspected and returns exit code 75 (tempfail). This is raised in claude_obsidian/transaction.py when _safe_file_state detects a race condition between two stat calls (lines 100–106).

TransactionRecoveryError indicates that a transaction rollback cannot be performed safely and returns exit code 3. This is raised when _confined_vault_unlink detects that a rollback target’s content or identity changed since it was recorded (lines 21–29).

Path Validation Error Codes

The system validates every write path through _normalize_vault_path and _assert_portable_write_path, emitting specific codes for each failure mode.

  • INVALID_WRITE_PATH: Raised when the path is empty, contains control characters, backslashes, or is absolute (lines 58–63).
  • NONCANONICAL_WRITE_PATH: Raised when the path is not in normalized POSIX form (lines 70–73).
  • NONCANONICAL_UNICODE_PATH: Raised when the path is not NFC-normalized Unicode (lines 75–77).
  • WRITE_PATH_TOO_LONG: Raised when the UTF-8 byte length exceeds the portable limit (lines 80–84).
  • UNPORTABLE_WRITE_PATH: Raised when the path contains characters unsafe on portable filesystems (:<>|?*"), ends with a dot or space, or matches a reserved device name (lines 96–105).
  • SYMLINK_WRITE_PATH: Raised by _assert_portable_write_path when a write would traverse a symlink or junction (lines 112–115).

Vault directory constraints trigger additional validation codes:

  • CASEFOLD_PATH_ALIAS: Raised by _assert_no_portable_vault_leaf_alias_at when a sibling entry aliases the target when case-folded (lines 86–99).
  • VAULT_DIRECTORY_LIMIT: Raised when a directory contains more than MAX_PORTABLE_SIBLING_ENTRIES entries (lines 78–83).
  • INVALID_VAULT_ROOT_NAME: Raised when the vault root name violates NFC, length, or control-character constraints (lines 64–74).

Operation and Identity Error Codes

Transaction metadata and vault identity verification use distinct error codes to signal contract violations.

  • INVALID_OPERATION_ID: Raised by safe_operation_id when the operation ID is missing or contains unsafe characters (lines 38–49).
  • UNSAFE_VAULT_IDENTITY: Raised by _vault_object_identity when the system cannot reliably pin a vault directory (lines 50–52).
  • VAULT_NOT_DIRECTORY: Raised when the vault path exists but is not a directory (lines 55–58).
  • VAULT_IDENTITY_CHANGED: Raised when the vault appeared or disappeared while its identity was being read (lines 63–66).
  • UNSAFE_VAULT_PATH: General code for any situation where a path cannot be safely inspected due to symlinks, non-directory parents, or permission errors (e.g., _safe_vault_path lines 31–34).

File Size and Content Error Codes

Read operations and transaction size limits emit specific codes to prevent unbounded memory consumption.

  • INVALID_READ_LIMIT: Raised by _read_vault_regular when the limit argument is not a positive integer (lines 38–41).
  • VAULT_FILE_MISSING: Raised when the requested vault file does not exist and missing_ok=False (lines 62–66).
  • VAULT_FILE_TOO_LARGE: Raised when a vault file exceeds the allowed read limit (lines 96–99).
  • TRANSACTION_FILE_TOO_LARGE: Raised by _safe_file_state when a file size exceeds MAX_TRANSACTION_FILE_BYTES during a transaction (lines 86–89).

Conflict and Recovery Error Codes

Runtime race conditions and rollback failures use dedicated exception types.

  • FILE_CHANGED_DURING_READ: A TransactionConflict raised by _safe_file_state when a file changes between two stat calls while being read (lines 100–106).
  • ROLLBACK_TARGET_CHANGED: A TransactionRecoveryError raised by _confined_vault_unlink when the rollback target’s content, type, or identity changed since it was recorded (lines 21–29).

Platform and Runtime Error Codes

Platform compatibility and runtime environment issues are detected before transaction execution.

  • UNSUPPORTED_PLATFORM: Raised by _require_write_platform when the host platform lacks required directory-descriptor confinement, such as native Windows (lines 16–18).
  • UNSAFE_RUNTIME_PATH: Raised by _safe_directory when runtime directory creation or inspection fails, such as when the system cannot create a safe parent directory (lines 84–96).
  • UNSAFE_LOCK_PATH: Raised via TransactionValidationError in lock-related functions like _require_lock_dirfd_support when lock-directory handling fails (lines 66–73).

Handling Transaction Errors in Python

Client code should catch specific exception types to implement appropriate retry or cleanup logic. All error codes are accessible via the .code attribute on exception instances.

from claude_obsidian.transaction import (
    TransactionValidationError,
    TransactionConflict,
    TransactionRecoveryError
)

def safe_vault_write(operation_id, path, content):
    try:
        # Attempt transaction

        pass
    except TransactionValidationError as e:
        # Handle pre-condition failures (exit code 2)

        if e.code == "INVALID_WRITE_PATH":
            print(f"Path validation failed: {e}")
        elif e.code == "UNSUPPORTED_PLATFORM":
            print("Platform lacks required confinement support")
        raise
    except TransactionConflict as e:
        # Handle race conditions (exit code 75)

        if e.code == "FILE_CHANGED_DURING_READ":
            print("File modified during inspection; retry suggested")
        raise
    except TransactionRecoveryError as e:
        # Handle rollback failures (exit code 3)

        if e.code == "ROLLBACK_TARGET_CHANGED":
            print("Rollback target modified; manual intervention required")
        raise

The test suite in tests/test_transaction.py exercises each error condition to ensure codes are emitted correctly, while tests/test_vault_ops.py and tests/test_windows_compat.py verify platform-specific behavior.

Summary

  • Four exception classes handle all transaction failures: TransactionError (base), TransactionValidationError (exit code 2), TransactionConflict (exit code 75), and TransactionRecoveryError (exit code 3).
  • 23 string error codes provide granular failure identification, ranging from INVALID_OPERATION_ID to ROLLBACK_TARGET_CHANGED.
  • Validation errors dominate the catalog, covering path normalization (NONCANONICAL_WRITE_PATH), Unicode safety (NONCANONICAL_UNICODE_PATH), and vault identity (UNSAFE_VAULT_IDENTITY).
  • Conflict detection uses FILE_CHANGED_DURING_READ to signal race conditions during file inspection.
  • Platform safety is enforced via UNSUPPORTED_PLATFORM for environments lacking directory-descriptor confinement.

Frequently Asked Questions

What is the difference between TransactionValidationError and TransactionConflict?

TransactionValidationError (exit code 2) is raised before any filesystem modifications occur, covering issues like invalid paths or unsupported platforms. TransactionConflict (exit code 75) is raised during active file inspection when _safe_file_state detects that a file changed between consecutive stat calls, indicating a concurrent modification.

Which error code indicates a file changed during reading?

The FILE_CHANGED_DURING_READ code is raised as a TransactionConflict by the _safe_file_state function in claude_obsidian/transaction.py (lines 100–106) when the system detects a race condition during file inspection.

How do I handle platform-specific errors on Windows?

Catch TransactionValidationError with code UNSUPPORTED_PLATFORM, which is raised by _require_write_platform (lines 16–18) when the host lacks directory-descriptor confinement. Native Windows without WSL typically triggers this code, requiring you to implement alternative safety mechanisms or abort the transaction.

What exit code is returned for rollback failures?

TransactionRecoveryError returns exit code 3 when a rollback cannot be performed safely. The specific ROLLBACK_TARGET_CHANGED code indicates that the target file’s content, type, or identity changed since the transaction recorded its state, making automatic recovery impossible.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →