Summary
Starting af code always emits a noisy WARNING + full traceback because _setup_infrastructure tries to open a read-only DuckDB connection to the same knowledge store file that already has an active read-write connection. DuckDB does not allow mixed read_only configurations on the same database file within a single process.
Reproduction
Observe in the output:
[WARNING] agentfox.engine.run: Failed to open read-only knowledge store for context assembly; falling back to main connection
…
_duckdb.ConnectionException: Connection Error: Can't open a connection to same database file with a different configuration than existing connections
Root Cause
In packages/agentfox/agentfox/engine/run.py, _setup_infrastructure:
- Line 109: opens a read-write connection —
open_knowledge_store(config.knowledge, read_only=False)
- Line 118: attempts a read-only connection to the same file —
open_knowledge_store(config.knowledge, read_only=True)
DuckDB (1.5.4) enforces that all in-process connections to a given file share the same read_only flag. The second call always fails.
The pattern was introduced in commit a053f869 for requirement 06-REQ-7.3 ("ensure assemble_context never holds a write lock"). The fallback on lines 119–123 catches the error and reuses the main connection, so the application runs — but:
- The traceback is alarming and confusing to users.
- The original intent (lock-free reads during context assembly) is silently defeated.
Impact
- Every
af code invocation prints a multi-line traceback that looks like a crash.
- The read-only separation goal of 06-REQ-7.3 is never achieved in practice.
Suggested Fix
Replace the separate read_only=True connection with a cursor from the existing read-write connection:
# Current (always fails):
context_knowledge_db = open_knowledge_store(config.knowledge, read_only=True)
# Proposed:
context_cursor = knowledge_db.connection.cursor()
DuckDB supports multiple cursors on a single connection, allowing concurrent reads without a separate connection object. The context assembly code would receive the cursor (or a thin wrapper) instead of a full KnowledgeDB instance.
This satisfies the spirit of 06-REQ-7.3 — context reads don't contend with write queries — without violating DuckDB's same-file configuration constraint.
Files Involved
packages/agentfox/agentfox/engine/run.py — _setup_infrastructure (primary fix site)
packages/agentfox/agentfox/knowledge/db.py — may need a helper to expose a cursor wrapper
Summary
Starting
af codealways emits a noisyWARNING+ full traceback because_setup_infrastructuretries to open a read-only DuckDB connection to the same knowledge store file that already has an active read-write connection. DuckDB does not allow mixedread_onlyconfigurations on the same database file within a single process.Reproduction
Observe in the output:
Root Cause
In
packages/agentfox/agentfox/engine/run.py,_setup_infrastructure:open_knowledge_store(config.knowledge, read_only=False)open_knowledge_store(config.knowledge, read_only=True)DuckDB (1.5.4) enforces that all in-process connections to a given file share the same
read_onlyflag. The second call always fails.The pattern was introduced in commit
a053f869for requirement 06-REQ-7.3 ("ensureassemble_contextnever holds a write lock"). The fallback on lines 119–123 catches the error and reuses the main connection, so the application runs — but:Impact
af codeinvocation prints a multi-line traceback that looks like a crash.Suggested Fix
Replace the separate
read_only=Trueconnection with a cursor from the existing read-write connection:DuckDB supports multiple cursors on a single connection, allowing concurrent reads without a separate connection object. The context assembly code would receive the cursor (or a thin wrapper) instead of a full
KnowledgeDBinstance.This satisfies the spirit of 06-REQ-7.3 — context reads don't contend with write queries — without violating DuckDB's same-file configuration constraint.
Files Involved
packages/agentfox/agentfox/engine/run.py—_setup_infrastructure(primary fix site)packages/agentfox/agentfox/knowledge/db.py— may need a helper to expose a cursor wrapper