affaan-m/ECC · error · Error
Failed to serialize
Error message
Failed to serialize ${label}: ${error.message} What it means
stringifyJson wraps JSON.stringify for values stored in JSON columns of the state store. If serialization throws — which happens for circular references, BigInt values, or other JSON-incompatible structures — the error is rethrown with the field label and the underlying message. This keeps inserts/upserts from failing with an opaque TypeError.
Solutions
- Remove or replace the circular/non-serializable field before passing the record
- Run JSON.stringify(value) yourself in a try/catch to find which label fails, then fix that field
- Convert BigInt fields with Number() or String() first; strip functions via a replacer: JSON.stringify(v, (k, val) => typeof val === 'function' ? undefined : val)
- For intentionally circular structures, store a flattened projection (ids only) instead of the live object graph
Example fix
// before
upsertSession({ id, snapshot: sessionState }); // sessionState has parent back-reference
// after
upsertSession({ id, snapshot: JSON.parse(JSON.stringify(sessionState, (k, v) => (typeof v === 'bigint' ? String(v) : v))) }); Defensive patterns
Strategy: try-catch
Validate before calling
function isJsonSafe(value, seen = new Set()) {
if (value === null || typeof value !== 'object') {
return typeof value !== 'function' && typeof value !== 'bigint';
}
if (seen.has(value)) return false;
seen.add(value);
return Object.values(value).every(v => isJsonSafe(v, seen));
} Type guard
const isJsonSafe = (v, seen = new Set()) =>
v === null || typeof v !== 'object'
? typeof v !== 'function' && typeof v !== 'bigint'
: !seen.has(v) && (seen.add(v), Object.values(v).every(x => isJsonSafe(x, seen))); Try / catch
try {
upsertSession(record);
} catch (err) {
if (err.message.startsWith('Failed to serialize')) {
console.error(`Field '${err.message.match(/serialize (.*?):/)?.[1]}' is not JSON-safe`);
record.snapshot = JSON.parse(JSON.stringify(record.snapshot, replacer));
return upsertSession(record);
} throw err;
} Prevention
- Strip functions and convert BigInt before storing objects in JSON columns
- Store ids/references instead of live object graphs with back-links
- Use a JSON replacer function for snapshots by convention
- Test record shapes against JSON.stringify in unit tests
When it happens
Trigger: Calling insertDecision, insertGovernanceEvent, upsertInstallState, upsertWorkItem, or upsertSession with a payload containing a circular reference (e.g. an object whose parent/child link points back), a BigInt, or a function-valued field.
Common situations: ORM/query builder result rows that include back-references to their client; attaching a logger or config object with circular deps into a snapshot; BigInt ids from a database driver stored raw; passing a class instance with getters that reference the instance itself.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- bundle values must be finite JSON data
- application request must be a JSON object
- candidate configuration exceeds 1 MiB
- Cannot merge ECC configuration at
- Cannot merge ECC configuration into invalid JSON at
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/a8f6c6421ad99d34.
Report an issue: GitHub.
Appendix: 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 8321021c54)