can1357/oh-my-pi · error · ToolError

SQLite column '${column}' only accepts JSON scalar values or

Error message

SQLite column '${column}' only accepts JSON scalar values or null

What it means

Thrown by normalizeWriteValue when a value supplied for a column write is not a JSON scalar (string, number, boolean, bigint) or null. Object/array values cannot be bound as SQLite parameters, so the tool rejects them up front rather than producing an opaque binding error.

Source

Thrown at packages/coding-agent/src/tools/sqlite-reader.ts:511

	if (!trimmed) return undefined;
	const violation = findWhereClauseViolation(trimmed);
	if (violation) {
		throw new ToolError(violation);
	}
	return trimmed;
}

function normalizeWriteValue(value: unknown, column: string): SqliteBinding {
	if (value === null) return null;
	if (
		typeof value === "string" ||
		typeof value === "number" ||
		typeof value === "boolean" ||
		typeof value === "bigint"
	) {
		return value;
	}
	throw new ToolError(`SQLite column '${column}' only accepts JSON scalar values or null`);
}

function validateWriteColumns(
	db: Database,
	table: string,
	data: Record<string, unknown>,
): Array<[string, SqliteBinding]> {
	const columns = new Set(getTableColumns(db, table));
	return Object.entries(data).map(([column, value]) => {
		if (!columns.has(column)) {
			throw new ToolError(`SQLite table '${table}' has no column named '${column}'`);
		}
		return [column, normalizeWriteValue(value, column)];
	});
}

export function parseSqlitePathCandidates(filePath: string): SqlitePathCandidate[] {
	const normalized = filePath.replace(/\\/g, "/");

View on GitHub (pinned to 9690622007)

Solutions

  1. JSON.stringify objects/arrays into strings before writing
  2. Extract scalars from the object and write individual columns
  3. Store arrays/objects as JSON text in a TEXT column
  4. Use null for absent values instead of objects

Example fix

// before
write('config', { meta: { a: 1 } })
// after
write('config', { meta: JSON.stringify({ a: 1 }) })
Defensive patterns

Strategy: type-guard

Validate before calling

function isBindable(v: unknown): boolean {
  return v === null || ['string','number','boolean','bigint'].includes(typeof v);
}
for (const [k, v] of Object.entries(data)) {
  if (!isBindable(v)) throw new Error(`column '${k}' needs a scalar or null; stringify objects first`);
}

Type guard

function isSqliteBinding(v: unknown): v is string | number | boolean | bigint | null {
  return v === null || typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || typeof v === 'bigint';
}

Try / catch

try {
  await writer.write('config', data);
} catch (err) {
  if (err instanceof ToolError && err.message.includes('only accepts JSON scalar values')) {
    // stringify nested values and retry once
  } else throw err;
}

Prevention

When it happens

Trigger: Writing data where a field is an object or array, e.g. data={meta:{a:1}} or data={tags:[1,2]}; passing undefined nested values; JSON payloads that keep nested structures instead of flattening or stringifying them.

Common situations: Inserting parsed JSON documents directly into a table; forgetting to JSON.stringify nested config blobs; ORM-style code that passes Date objects or arrays.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0d47e5e502114c68. Report an issue: GitHub.