{"record":{"id":"199d670fa4d73a7b","repo":"can1357/oh-my-pi","slug":"sqlite-write-content-must-be-a-json-object","errorCode":null,"errorMessage":"SQLite write content must be a JSON object","messagePattern":"SQLite write content must be a JSON object","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/write.ts","lineNumber":797,"sourceCode":"\t\t\t\t\tlookup.kind === \"pk\"\n\t\t\t\t\t\t? deleteRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key)\n\t\t\t\t\t\t: deleteRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key);\n\t\t\t\tresultText =\n\t\t\t\t\tdeleted > 0\n\t\t\t\t\t\t? `Deleted row '${resolvedSqlitePath.key}' from ${resolvedSqlitePath.table}`\n\t\t\t\t\t\t: `No row deleted from ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;\n\t\t\t} else {\n\t\t\t\tlet parsedContent: unknown;\n\t\t\t\ttry {\n\t\t\t\t\tparsedContent = Bun.JSON5.parse(content);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow new ToolError(\n\t\t\t\t\t\t`SQLite write content must be valid JSON5: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (!isRecord(parsedContent)) {\n\t\t\t\t\tthrow new ToolError(\"SQLite write content must be a JSON object\");\n\t\t\t\t}\n\n\t\t\t\tif (resolvedSqlitePath.key) {\n\t\t\t\t\tconst lookup = resolveTableRowLookup(db, resolvedSqlitePath.table);\n\t\t\t\t\tconst updated =\n\t\t\t\t\t\tlookup.kind === \"pk\"\n\t\t\t\t\t\t\t? updateRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key, parsedContent)\n\t\t\t\t\t\t\t: updateRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key, parsedContent);\n\t\t\t\t\tresultText =\n\t\t\t\t\t\tupdated > 0\n\t\t\t\t\t\t\t? `Updated row '${resolvedSqlitePath.key}' in ${resolvedSqlitePath.table}`\n\t\t\t\t\t\t\t: `No row updated in ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;\n\t\t\t\t} else {\n\t\t\t\t\tinsertRow(db, resolvedSqlitePath.table, parsedContent);\n\t\t\t\t\tresultText = `Inserted row into ${resolvedSqlitePath.table}`;\n\t\t\t\t}\n\t\t\t}\n","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/write.ts#L779-L815","documentation":"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.","triggerScenarios":"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'\".","commonSituations":"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.","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."],"exampleFix":"// before\ncontent: \"[1, 2, 3]\"\n// after\ncontent: \"{ rows: [1, 2, 3] }\"","handlingStrategy":"validation","validationCode":"const parsed = Bun.JSON5.parse(content);\nif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n  throw new Error(\"SQLite write content must be a JSON object\");\n}","typeGuard":"function isRecord(v: unknown): v is Record<string, unknown> {\n  return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["sqlite","validation","json"],"backgroundTag":"json-payload-not-an-object","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}