can1357/oh-my-pi · error · ToolError

error instanceof Error ? error.message : String(error) (wrap

Error message

error instanceof Error ? error.message : String(error) (wraps underlying error)

What it means

readSqlite wraps any non-ToolError thrown inside the read (SQLite driver errors, malformed DB files, SQL failures in raw queries) into a ToolError whose message is error.message (or String(error)). The original stack/type is discarded, so callers only see the flattened message string.

Source

Thrown at packages/coding-agent/src/tools/read-sqlite.ts:211

					table: "query",
					dbPath: resolvedSqlitePath.absolutePath,
				});
				if (result.truncated) {
					output += `\n[Output capped at ${MAX_RAW_QUERY_ROWS} rows; add a LIMIT/OFFSET clause to the query to page through more]`;
				}
				return toolResult<ReadToolDetails>(details)
					.text(prependSuffixResolutionNotice(output, resolvedSqlitePath.suffixResolution))
					.sourcePath(resolvedSqlitePath.absolutePath)
					.done();
			}
		}

		throw new ToolError("Unsupported SQLite selector");
	} catch (error) {
		if (error instanceof ToolError) {
			throw error;
		}
		throw new ToolError(error instanceof Error ? error.message : String(error));
	} finally {
		db?.close();
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the wrapped message — it usually names the SQLite failure (e.g. 'file is not a database', 'no such table').
  2. Verify the file is a real SQLite DB (magic header 'SQLite format 3\u0000').
  3. Fix the SQL in :raw selectors (check table/column names via :schema first).
  4. Close other connections holding a write lock; retry if it was transient 'database is locked'.
  5. If the stack trace matters, run outside the tool wrapper (e.g. sqlite3 CLI) to see full details.

Example fix

// before: raw unvetted SQL
file.db:raw?sql=SELEC * FROM users
// after: validated SQL and correct names
file.db:raw?sql=SELECT * FROM users LIMIT 20
Defensive patterns

Strategy: try-catch

Validate before calling

import { $ } from 'bun';
// verify it is a SQLite database before reading
const header = Buffer.from(await Bun.file(dbPath).slice(0, 16).arrayBuffer());
if (!header.toString('latin1').startsWith('SQLite format 3')) {
  throw new Error(`${dbPath} is not a SQLite database`);
}

Type guard

function isSqliteFile(header: Uint8Array): boolean {
  return Array.from(header.slice(0, 16)).join(',') ===
    Array.from(Buffer.from('SQLite format 3\u0000', 'latin1')).join(',');
}

Try / catch

try {
  return await readSqlite(dbPath, selector);
} catch (e) {
  if (e instanceof ToolError) {
    // message is the flattened sqlite driver error
    if (/file is not a database/i.test(e.message)) { /* wrong file type */ }
    if (/database is locked/i.test(e.message)) { /* retry after releasing writer */ }
  }
  throw e;
}

Prevention

When it happens

Trigger: executeReadQuery on invalid SQL in a :raw selector; queryRows/getTableSchema hitting a corrupt or encrypted (non-SQLite) file; native sqlite driver throwing (disk I/O, locked DB); any bug in helper functions inside the try block.

Common situations: Passing a text file, WAL-only remnants, or an encrypted SQLCipher database where SQLite header magic fails; writing SELECT with a typo in :raw mode; database locked by another writer.

Related errors


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