Claude-Obsidian Capture Queue States: The Four States Every Entry Must Follow

Each entry in the claude-obsidian capture queue must exist in exactly one of four explicit states—queued, claimed, completed, or failed—as enforced by the QUEUE_STATES frozenset in the source code.

The claude-obsidian project implements a robust capture queue system for processing content entries asynchronously. According to the source code in claude_obsidian/capture.py, the system enforces a strict state machine that governs every entry's lifecycle from creation to final resolution. Understanding these four possible states is essential for developers integrating with the capture API or debugging queue behavior.

The Four Capture Queue States

Entries in the capture queue transition through a predefined lifecycle defined by four string identifiers. These values represent the complete set of valid states; no other values are permitted by the validation logic.

queued indicates the entry has been created and is waiting for a worker to claim it. At this stage, no processing has begun, and the entry is available for pickup by any compatible worker.

claimed signals that a worker has actively taken ownership of the entry. When an entry enters this state, the system records claim metadata including the worker's PID, host identifier, and timestamp to prevent duplicate processing.

completed marks successful processing, indicating the worker finished its task without errors. Entries in this state carry a result object containing the output of the capture operation.

failed designates that processing ended with an unrecoverable error. Unlike transient failures, entries reaching this state will not automatically retry and instead store an error object documenting the failure reason.

State Definitions in Source Code

The claude-obsidian source code centrally defines the allowed states at line 75 of claude_obsidian/capture.py using an immutable set:

QUEUE_STATES = frozenset({"queued", "claimed", "completed", "failed"})

This constant serves as the single source of truth for valid state values throughout the application. The validation logic resides in the _validate_queue_entry function (lines 82-84), which checks incoming entries against this set:

state = entry.get("state")
if state not in QUEUE_STATES:
    raise CaptureValidationError("QUEUE_INVALID", f"invalid queue state for {item_id}")

Any attempt to insert or update an entry with a state not present in QUEUE_STATES triggers a CaptureValidationError with the code "QUEUE_INVALID", ensuring data integrity at the API boundary.

Working with States in Practice

When creating new queue entries, initialize the state field to "queued" and ensure your dictionary structure complies with the schema enforced by _validate_queue_entry:

from claude_obsidian.capture import QUEUE_STATES

new_entry = {
    "id": "a1b2c3d4e5f6071829abcde0",
    "idempotency_key": "my-capture-run-1",
    "adapter": "filesystem",
    "source": "inbox/example.txt",
    "state": "queued",
    "attempts": 0,
    "created_at": "2026-08-29T12:00:00Z",
    "updated_at": "2026-08-29T12:00:00Z",
    "metadata": {},
}
assert new_entry["state"] in QUEUE_STATES

Workers transition entries from queued to claimed by updating the state field and appending claim metadata:

def claim_entry(entry, pid, host):
    entry["state"] = "claimed"
    entry["claim"] = {
        "token": "deadbeefcafebabe1122334455667788",
        "pid": pid,
        "host": host,
        "claimed_at": "2026-08-29T12:01:00Z",
        "claimed_at_epoch": 1724941260,
    }
    entry["updated_at"] = "2026-08-29T12:01:00Z"

Upon successful completion, update the state to "completed" and attach the result object:

def complete_entry(entry, result):
    entry["state"] = "completed"
    entry["result"] = result  # must be a dict

    entry["updated_at"] = "2026-08-29T12:02:00Z"

For unrecoverable failures, set the state to "failed" and include error details:

def fail_entry(entry, error):
    entry["state"] = "failed"
    entry["error"] = {"message": error, "retryable": False}
    entry["updated_at"] = "2026-08-29T12:03:00Z"

State Validation and Error Handling

The strict state model prevents invalid data from entering the queue system. When _validate_queue_entry encounters an undefined state value, it immediately raises CaptureValidationError before the entry reaches persistent storage. This validation occurs during both initial creation and subsequent updates, ensuring that state transitions always move between the four defined values.

The CaptureValidationError exception includes a specific error code ("QUEUE_INVALID") and a descriptive message containing the offending item ID, making it straightforward to identify and correct schema violations in client code.

Summary

  • Four exclusive states: queued, claimed, completed, and failed constitute the complete set of valid states for any capture queue entry.
  • Immutable definition: The QUEUE_STATES frozenset at line 75 of claude_obsidian/capture.py serves as the authoritative whitelist.
  • Strict validation: The _validate_queue_entry function rejects any state value not present in QUEUE_STATES by raising CaptureValidationError.
  • State metadata: The claimed state requires additional claim metadata (PID, host, token), while completed and failed states attach result or error objects respectively.
  • No automatic retry: Entries reaching the failed state remain terminal; the system does not implement automatic retry logic for failed captures.

Frequently Asked Questions

What happens if I attempt to use a state value not in the four allowed states?

The _validate_queue_entry function will raise a CaptureValidationError with the code "QUEUE_INVALID" and reject the entry. This validation occurs at lines 82-84 of claude_obsidian/capture.py, preventing any undefined state values from persisting in the queue.

Can a failed entry be retried or moved back to the queued state?

According to the source code analysis, entries in the failed state are considered terminal with retryable set to False in the error object. The system does not implement automatic retry mechanisms for failed captures, though client applications could theoretically create a new entry with a fresh idempotency key.

How does the claimed state prevent multiple workers from processing the same entry?

When an entry transitions to claimed, the worker must attach a unique claim token along with its PID and host identifier to the entry's claim field. This metadata acts as a distributed lock; other workers checking the queue should skip entries already in the claimed state, preventing duplicate processing across the worker pool.

Where can I find the complete state definitions and validation logic?

All state definitions and validation functions reside in claude_obsidian/capture.py. The QUEUE_STATES constant appears at line 75, while the _validate_queue_entry validation logic spans lines 82-84. For test examples of state transitions, consult tests/test_capture.py in the repository.

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 →