can1357/oh-my-pi · error · ToolError
SQLite write content must be a JSON object
Error message
SQLite write content must be a JSON object
What it means
The WriteTool's SQLite branch (path like sqlite://...) parses the `content` argument as JSON5 and then requires the parsed result to be a plain object. A JSON5 scalar (number, string, boolean, null) or array is rejected because the writer needs key/value pairs to map onto table columns or a JSON document. It is thrown as a ToolError so the agent sees it as a recoverable tool failure.
Source
Thrown at packages/coding-agent/src/tools/write.ts:797
lookup.kind === "pk"
? deleteRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key)
: deleteRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key);
resultText =
deleted > 0
? `Deleted row '${resolvedSqlitePath.key}' from ${resolvedSqlitePath.table}`
: `No row deleted from ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;
} else {
let parsedContent: unknown;
try {
parsedContent = Bun.JSON5.parse(content);
} catch (error) {
throw new ToolError(
`SQLite write content must be valid JSON5: ${error instanceof Error ? error.message : String(error)}`,
);
}
if (!isRecord(parsedContent)) {
throw new ToolError("SQLite write content must be a JSON object");
}
if (resolvedSqlitePath.key) {
const lookup = resolveTableRowLookup(db, resolvedSqlitePath.table);
const updated =
lookup.kind === "pk"
? updateRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key, parsedContent)
: updateRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key, parsedContent);
resultText =
updated > 0
? `Updated row '${resolvedSqlitePath.key}' in ${resolvedSqlitePath.table}`
: `No row updated in ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;
} else {
insertRow(db, resolvedSqlitePath.table, parsedContent);
resultText = `Inserted row into ${resolvedSqlitePath.table}`;
}
}
View on GitHub (pinned to 9690622007)
Solutions
- Wrap the payload in an object, e.g. `{"rows":[1,2,3]}` or a single record object keyed by column names.
- If a multi-row insert is intended, pass `{"table":"t","rows":[...]}` or the format documented for the sqlite:// path.
- Validate the JSON5 parses to an object before calling the tool.
Example fix
// before
content: "[1, 2, 3]"
// after
content: "{ rows: [1, 2, 3] }" Defensive patterns
Strategy: validation
Validate before calling
const parsed = Bun.JSON5.parse(content);
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
throw new Error("SQLite write content must be a JSON object");
} Type guard
function isRecord(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
} Prevention
- Always build SQLite write payloads as top-level objects with column-name keys.
- Use arrays inside an object (e.g. { rows: [...] }) for multi-row operations.
- Validate payloads with JSON5 round-trip before calling the tool.
When it happens
Trigger: Calling the write tool with a sqlite:// target where `content` parses successfully as JSON5 but is not an object — e.g. content="[1,2,3]", content="42", or content="'just a string'".
Common situations: Models pass array payloads intending multi-row inserts, quote-escaped plain strings, or null/numeric literals when writing to a JSON document column; also hand-crafted RPC/SDK calls to the write tool that skip object normalization.
Related errors
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains an empty provider
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id wit
- Validation failed for tool "${toolCall.name}": Tool call arg
- ${destination} returned an invalid response
- Invalid array JSON: ${rawValue}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/199d670fa4d73a7b.
Report an issue: GitHub.