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

> Understand the four states queued claimed completed and failed for entries in the claude-obsidian capture queue. Learn how the source code enforces these states.

- Repository: [Agrici.Daniel/claude-obsidian](https://github.com/AgriciDaniel/claude-obsidian)
- Tags: internals
- Published: 2026-08-29

---

**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`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/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`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/capture.py) using an immutable set:

```python
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:

```python
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`:

```python
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:

```python
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:

```python
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:

```python
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`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/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`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/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`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/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`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/tests/test_capture.py) in the repository.