abhigyanpatwari/GitNexus · error · Error
Write operations are not allowed. The pool adapter is read-o
Error message
Write operations are not allowed. The pool adapter is read-only.
What it means
Thrown by executeParameterized when the underlying LadybugDB error is classified as a read-only DB error by isReadOnlyDbError. The pool adapter opens databases in read-only mode for query serving (MCP/serve contexts); any write operation (CREATE, MERGE, DELETE, SET, REMOVE, DROP, COPY) triggers a native read-only violation. The original native error is preserved as the `cause` property so the original stack frame and message are not lost. This is a user-facing guard: the pool adapter is intentionally read-only and must never be used for writes.
Source
Thrown at gitnexus/src/core/lbug/pool-adapter.ts:1056
const conn = await checkout(entry);
silenceStdout();
activeQueryCount++;
let queryResult: lbug.QueryResult | lbug.QueryResult[] | undefined;
try {
const stmt = await withTimeout(conn.prepare(cypher), QUERY_TIMEOUT_MS, 'Prepare');
if (!stmt.isSuccess()) {
const errMsg = await stmt.getErrorMessage();
throw new Error(`Prepare failed: ${errMsg}`);
}
queryResult = await withTimeout(conn.execute(stmt, params), QUERY_TIMEOUT_MS, 'Execute');
const result = Array.isArray(queryResult) ? queryResult[0] : queryResult;
const rows = await result.getAll();
return rows;
} catch (err) {
if (isReadOnlyDbError(err)) {
// Preserve the native error as `cause` so the original frame/message is
// not lost behind the friendly read-only message (#2068 follow-up).
throw new Error('Write operations are not allowed. The pool adapter is read-only.', {
cause: err,
});
}
throw err;
} finally {
// Close the native QueryResult cursor(s) before returning the connection —
// getAll() drains rows but does not release the native cursor, so without
// this the cursor leaks for the connection's lifetime (#2068 follow-up).
// Best-effort via the shared helper; never masks the query result or a real
// error.
if (queryResult) await closeQueryResults(queryResult);
activeQueryCount--;
restoreStdout();
checkin(entry, conn);
}
};
/**View on GitHub (pinned to d540b00184)
Solutions
- Ensure write operations go through the singleton adapter (lbug-adapter.ts withLbugDb / executeQuery), not the pool adapter
- If you're calling executeParameterized from serve/MCP code, verify the query is read-only (MATCH/RETURN/WITH only)
- If a write is genuinely needed, run `gitnexus analyze` which uses the write-capable singleton adapter
- Check the `cause` property of the error for the original native LadybugDB message — it confirms the operation was a write
Example fix
// before — write query through read-only pool adapter
await executeParameterized(repoId, 'CREATE (n:Test {name: $name})', { name: 'x' });
// after — use the write-capable singleton adapter path (during analyze)
await withLbugDb(async (c) => { await c.query('CREATE (n:Test {name: "x"})'); }); Defensive patterns
Strategy: validation
Validate before calling
// Detect write operations in Cypher before sending to the read-only pool
const WRITE_CLAUSES = /^(CREATE|MERGE|DELETE|SET|REMOVE|DROP|COPY|INSERT|UPDATE)\b/i;
function isWriteQuery(cypher: string): boolean {
return WRITE_CLAUSES.test(cypher.trim());
}
// Before calling executeParameterized:
if (isWriteQuery(cypher)) {
throw new Error('Write queries must go through the singleton adapter, not the read-only pool');
} Try / catch
try {
const rows = await executeParameterized(repoId, cypher, params);
} catch (e) {
if (e instanceof Error && e.message.includes('read-only')) {
// Routed a write to the pool — use the singleton adapter instead
logger.error('Attempted write on read-only pool — use withLbugDb for writes');
}
throw e;
} Prevention
- Enforce a code-level separation: pool adapter for reads (serve/MCP), singleton adapter for writes (analyze)
- Add a pre-query check that rejects write clauses in the pool adapter
- Review code changes that modify Cypher in serve/MCP paths — ensure no write clauses sneak in
- Use the `cause` property on the error to inspect the original native LadybugDB read-only message
When it happens
Trigger: Calling executeParameterized (the pool adapter's query function) with a Cypher query that performs a write operation — CREATE, MERGE with create, DELETE, SET, REMOVE, DROP, or COPY FROM — in a read-only serve/MCP context. The pool adapter is used by `gitnexus serve` and the MCP server; writes go through the singleton adapter (lbug-adapter.ts) during analyze, not the pool.
Common situations: An MCP tool or serve endpoint accidentally issues a write query through the pool adapter instead of the singleton adapter; a developer testing queries against a serve instance with a mutation; a bug where a read query was rewritten to include a MERGE/SET clause; calling the wrong adapter function for a write operation.
Related errors
- LadybugDB checkpoint sidecar is missing for ${dbPath}. Rebui
- LadybugDB WAL corruption detected for ${repoId}. Run `gitnex
- LadybugDB not found at ${dbPath}. Run: gitnexus analyze
- LadybugDB WAL corruption detected for ${repoId}. WAL corrupt
- LadybugDB unavailable for ${repoId}. Another process may be
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/95df5d6678b24df3.
Report an issue: GitHub.