affaan-m/ECC · error
Failed to serialize ${label}: ${error.message}
Error message
Failed to serialize ${label}: ${error.message} What it means
Thrown by stringifyJson() in the state-store queries module when JSON.stringify fails on a value the caller asked to write into a JSON column. The try/catch wraps the native call so the surfaced error names the column label rather than reporting a bare 'Converting circular structure to JSON'. The helper is used to serialize snapshots and other structured fields before they go into SQLite.
Source
Thrown at scripts/lib/state-store/queries.js:36
throw new Error(`Invalid limit: ${value}`);
}
return parsed;
}
function parseJsonColumn(value, fallback) {
if (value === null || value === undefined || value === '') {
return fallback;
}
return JSON.parse(value);
}
function stringifyJson(value, label) {
try {
return JSON.stringify(value);
} catch (error) {
throw new Error(`Failed to serialize ${label}: ${error.message}`);
}
}
function mapSessionRow(row) {
const snapshot = parseJsonColumn(row.snapshot, {});
return {
id: row.id,
adapterId: row.adapter_id,
harness: row.harness,
state: row.state,
repoRoot: row.repo_root,
startedAt: row.started_at,
endedAt: row.ended_at,
snapshot,
workerCount: Array.isArray(snapshot && snapshot.workers) ? snapshot.workers.length : 0,
};
}
View on GitHub (pinned to 01e15490f0)
Solutions
- Strip circular references before persisting: build a fresh plain object picking only the fields you need.
- Convert BigInt values to String or Number before serializing, or use a replacer: JSON.stringify(value, (_, v) => typeof v === 'bigint' ? v.toString() : v).
- Use util.inspect or a safe-stringify library to detect circular structure at debug time, then fix the source shape.
- Unit-test the serialization path with a representative payload so the failure surfaces before it reaches the DB writer.
Example fix
// before
stringifyJson(sessionWithCircularRef, 'snapshot');
// -> Failed to serialize snapshot: Converting circular structure to JSON
// after
const { snapshot } = sessionWithCircularRef;
const safeSnapshot = {
workers: (snapshot.workers || []).map(({ name, status, branch }) => ({ name, status, branch })),
};
stringifyJson(safeSnapshot, 'snapshot'); Defensive patterns
Strategy: try-catch
Validate before calling
function isSafeJson(value) {
try {
JSON.stringify(value);
return true;
} catch {
return false;
}
}
if (!isSafeJson(snapshot)) {
// strip known circular fields or build a plain-object copy
snapshot = { workers: snapshot.workers.map(w => ({ name: w.name, status: w.status })) };
}
stringifyJson(snapshot, 'snapshot'); Type guard
function isPlainSerializable(value, seen = new WeakSet()) {
if (value === null || typeof value !== 'object') return true;
if (typeof value === 'function' || typeof value === 'bigint') return false;
if (seen.has(value)) return false;
seen.add(value);
return Object.values(value).every(v => isPlainSerializable(v, seen));
} Try / catch
try {
stringifyJson(value, label);
} catch (error) {
if (/Failed to serialize/.test(error.message)) {
// fall back to a safe replacer or drop the field
return JSON.stringify(value, (_, v) => typeof v === 'bigint' ? v.toString() : v);
}
throw error;
} Prevention
- Never persist objects that reference their parent — build a fresh snapshot object.
- Convert BigInt IDs to String before they enter the snapshot path.
- Add a unit test that JSON.stringify's every payload shape you persist.
- Use a WeakSet-based cycle detector in dev builds to surface cycles early.
When it happens
Trigger: Passing an object with a circular reference (e.g. a session object that references itself, a DOM-like node, or an object that includes its parent); passing a value containing a BigInt (JSON.stringify throws on BigInt by default); passing a value with a .toJSON() that itself throws.
Common situations: Storing a session snapshot that inadvertently includes the parent session record; serializing a worker plan whose templateVariables reference the plan object; mixing BigInt row IDs into a JSON payload after a migration to bigint; a custom class whose toJSON override errors on missing fields.
Related errors
- ECC_PROJECT_DIR must be a child path within /workspace.
- Unknown argument: ${arg}
- ${source} is not valid JSON: ${error.message}
- ${source} is missing the catalog count description
- Invalid JSON in ${label}: ${error.message}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/a8f6c6421ad99d34.
Report an issue: GitHub.