affaan-m/ECC · error · SyntaxError
Malformed coordination JSON in body
Error message
Malformed coordination JSON in body: ${error.message} — raw: ${match[1].slice(0, 120)} What it means
extractCoordinationState finds an embedded coordination JSON block in an issue/PR body and throws a SyntaxError when that block fails JSON.parse. The message includes the underlying parse error plus the first 120 characters of the raw block to help locate the corruption. The library throws rather than silently ignoring the block because the coordination state is authoritative for claim/conflict decisions.
Solutions
- Read the 'raw:' excerpt in the message to find the exact JSON syntax error.
- Fix the coordination JSON block in the issue/PR body (validate with a JSON linter before saving).
- Restore the block by re-running the command that originally wrote the coordination state.
- If the block is unrecoverable, remove it and let the library re-initialize fresh coordination state.
Example fix
// before (in issue body)
<!-- ecc:coordination -->{"status": "claimed",, "owner": "alice"}
// after
<!-- ecc:coordination -->{"status": "claimed", "owner": "alice"} Defensive patterns
Strategy: validation
Validate before calling
function isParseableJson(s) {
try { JSON.parse(s); return true; } catch { return false; }
}
const block = body.match(/<!--\s*ecc:coordination\s*-->([\s\S]*?)<!--\s*\/ecc:coordination\s*-->/);
if (block && !isParseableJson(block[1])) throw new Error('Coordination block JSON is malformed; fix the issue body'); Type guard
const isCoordinationState = (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && typeof v.status === 'string';
Try / catch
let state;
try {
state = extractCoordinationState(body);
} catch (e) {
if (e instanceof SyntaxError && e.message.startsWith('Malformed coordination JSON')) {
console.error('Fix the coordination JSON block in the issue body:', e.message);
state = null;
} else { throw e; }
} Prevention
- Never hand-edit the coordination JSON block; use the CLI commands that write it.
- Validate JSON with a linter before saving issue-body edits.
- Keep the fenced block intact when reformatting markdown.
- Re-run the state-writing command to regenerate corrupted blocks.
When it happens
Trigger: Calling getCoordinationState (or functions that read state from a body) when the fenced coordination JSON in the body was hand-edited, truncated, contains trailing commas/comments, or was mangled by another tool.
Common situations: Manual edits to the issue body that break the JSON (missing brace/quote), markdown formatters or GitHub UI edits reflowing the block, automation writing partial state before crashing, or copying the block from another issue with encoding damage.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cannot read JSON object
- gh returned invalid JSON
- gh returned invalid JSON
- Memory frontmatter field in
- Nasiko manifest is not valid JSON.
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/dccb2e87f1287efe.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/github-coordination/parsing.js:26
function normalizeBodyForComparison(body) {
return (body || '').replace(/"lastSyncAt"\s*:\s*[^,}\n]+/g, '"lastSyncAt": NORMALIZED');
}
function extractCoordinationState(body, policy = DEFAULT_POLICY) {
const marker = escapeRegExp(policy.sectionMarker || DEFAULT_SECTION_MARKER);
const regex = new RegExp(`<!--\\s*${marker}:start\\s*-->\\s*` + '```json\\s*([\\s\\S]*?)\\s*```' + `\\s*<!--\\s*${marker}:end\\s*-->`, 'm');
const match = String(body || '').match(regex);
if (!match) {
return null;
}
try {
const parsed = JSON.parse(match[1]);
return parsed && typeof parsed === 'object' ? parsed : null;
} catch (error) {
throw new SyntaxError(`Malformed coordination JSON in body: ${error.message} — raw: ${match[1].slice(0, 120)}`);
}
}
function extractIssueReferences(text) {
const refs = new Set();
const source = String(text || '');
for (const match of source.matchAll(/(?:^|[^\d])#(\d+)\b/g)) {
refs.add(Number.parseInt(match[1], 10));
}
return Array.from(refs)
.filter(Number.isFinite)
.sort((a, b) => a - b);
}
function extractTasks(body) {
const lines = String(body || '').split(/\r?\n/);
const tasks = [];
let inTasks = false;View on GitHub (pinned to 8321021c54)