How ThreadAlreadyExistsError and StateConflictError Protect Thread Integrity in aisuite
ThreadAlreadyExistsError prevents overwriting existing conversation threads, while StateConflictError blocks concurrent writes through optimistic concurrency control, ensuring immutable and consistent AI agent execution state.
The aisuite library treats every persisted agent execution as an immutable thread. To enforce this immutability guarantee and prevent race conditions in concurrent environments, the framework raises two specific exceptions: ThreadAlreadyExistsError when attempting to recreate an existing thread, and StateConflictError when optimistic concurrency checks fail during state updates.
ThreadAlreadyExistsError: Preventing Duplicate Thread Creation
Definition and Location
ThreadAlreadyExistsError is defined in [aisuite/agents/runner.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py) (lines 30-44). This exception enforces the rule that once a thread is persisted to a StateStore, it cannot be reinitialized with the same thread_id.
When It Triggers
The check occurs inside the Runner._run_impl method. When you call Runner.run() with both a state_store and thread_id parameter, the code verifies whether state already exists for that identifier:
if state_store is not None and state_store.load_state(thread_id) is not None:
raise ThreadAlreadyExistsError(...)
If the store returns a non-None state object, the error immediately halts execution. This forces you to use Runner.continue_sync() or Runner.continue() instead, ensuring the original conversation history is preserved and never accidentally overwritten by a fresh run.
StateConflictError: Optimistic Concurrency Control
Definition and Location
StateConflictError is defined in [aisuite/agents/state_store.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/state_store.py) (lines 15-19). This exception prevents lost updates when multiple processes or workers attempt to write to the same thread simultaneously.
Revision Checking Mechanism
The error originates in the _assert_revision helper function (lines 71-80), which validates that the expected revision matches the current stored revision before save_state completes. When StateStore.save_state is invoked with a revision parameter that does not match the stored value, the helper raises StateConflictError:
_assert_revision(thread_id, current.revision if current else None, revision)
This aborts the write operation, allowing the caller to load the latest state and retry with the correct revision number. Both InMemoryStateStore and FileStateStore implement this check to protect against race conditions in multi-worker deployments.
Architectural Flow: How the Errors Work Together
The thread protection mechanism operates across three distinct phases:
-
Thread Initialization – When
Runner.run()is called with athread_id,Runner._run_implqueries the store. Ifload_statereturns existing data,ThreadAlreadyExistsErrorraises immediately, preventing duplicate creation. -
State Persistence – After a run completes,
Runner._run_implcallsstate_store.save_state. The concrete store implementations execute_assert_revisionto verify the writer has the latest version. If another process modified the thread since this client loaded it,StateConflictErrorraises. -
Thread Continuation – When
Runner.continue_*is used, the existingStoredRunStateis loaded with its current revision. After appending new messages, the final state saves with the previous revision passed as therevisionparameter, enabling the optimistic concurrency check to detect any intermediate modifications.
Code Examples
Handling ThreadAlreadyExistsError
Catch this exception to switch from creating a new thread to continuing an existing one:
import aisuite as ai
from aisuite.agents import Runner
try:
result = Runner.run(
agent=my_agent,
input="Start a new conversation",
state_store=ai.InMemoryStateStore(),
thread_id="demo-thread",
)
except ai.ThreadAlreadyExistsError as exc:
# The thread already exists – continue instead
result = Runner.continue_sync(
target=my_agent,
input="Add another question",
state_store=ai.InMemoryStateStore(),
thread_id="demo-thread",
)
print(result.final_output)
The except block catches the error and switches to a continuation path, ensuring the prior state is retained.
Retrying StateConflictError with Back-off
Implement retry logic to handle concurrent writes safely:
import time
from aisuite.agents import Runner, InMemoryStateStore, StateConflictError
store = InMemoryStateStore()
thread_id = "shared-thread"
# First run – creates the thread
Runner.run_sync(agent=my_agent, input="Hello", state_store=store, thread_id=thread_id)
# Simulate two workers trying to write concurrently
def safe_save(new_input):
while True:
try:
# Load current state and its revision
stored = store.load_state(thread_id)
rev = stored.revision if stored else None
# Continue the thread with the expected revision
return Runner.continue_sync(
target=my_agent,
input=new_input,
state_store=store,
thread_id=thread_id,
revision=rev, # <-- passed to StateStore.save_state
)
except StateConflictError:
# Someone else wrote in the meantime – retry after a short back‑off
time.sleep(0.1)
result = safe_save("What is the weather?")
print(result.final_output)
The loop retries when StateConflictError occurs, guaranteeing that the final write succeeds with the latest revision.
Summary
- ThreadAlreadyExistsError in
aisuite/agents/runner.pyprevents accidental overwrites by requiringRunner.continue_*for existing threads rather thanRunner.run. - StateConflictError in
aisuite/agents/state_store.pyimplements optimistic concurrency via the_assert_revisionhelper, blocking writes that would cause lost updates. - Together they ensure thread immutability and prevent race conditions in distributed, multi-process workloads.
- Both exceptions are exported publicly in
aisuite/__init__.pyandaisuite/agents/__init__.pyfor convenient access asai.ThreadAlreadyExistsErrorandai.StateConflictError.
Frequently Asked Questions
What is the difference between ThreadAlreadyExistsError and StateConflictError?
ThreadAlreadyExistsError prevents creating a new thread with an ID that already exists in the store, while StateConflictError prevents concurrent modifications to an existing thread by validating revision numbers during save operations according to the aisuite source code.
How do I continue an existing conversation in aisuite?
Catch ThreadAlreadyExistsError and use Runner.continue_sync() or Runner.continue() with the same thread_id and state_store parameters. This appends new messages to the existing conversation history rather than replacing it.
Can multiple workers safely write to the same thread?
Yes, but you must implement retry logic around StateConflictError. Load the latest state to obtain the current revision, then retry the continue operation. The _assert_revision check in state_store.py ensures only writers with the latest revision succeed.
Where are these exceptions defined in the source code?
ThreadAlreadyExistsError is defined in [aisuite/agents/runner.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/runner.py), while StateConflictError is defined in [aisuite/agents/state_store.py](https://github.com/andrewyng/aisuite/blob/main/aisuite/agents/state_store.py). Both are re-exported in aisuite/__init__.py for easy importing.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →