nexu-io/open-design · error
proposal patch.after is not valid JSON
Error message
proposal patch.after is not valid JSON
What it means
Thrown by parseJsonPatchAfter() when proposal.patch.format === 'json' but proposal.patch.after is not parseable as JSON. The function is called by applyMemoryProposal and applyAutomationTemplateProposal to extract structured fields from the patch body. Markdown-format patches skip parsing and never hit this.
Source
Thrown at apps/daemon/src/automation-proposals.ts:140
if (proposal.status === 'pending-review' || proposal.status === 'draft') return;
throw new Error(`proposal ${proposal.id} is ${proposal.status}, not reviewable`);
}
function safeMemoryType(value: unknown): MemoryType {
return typeof value === 'string' && VALID_MEMORY_TYPES.has(value as MemoryType)
? (value as MemoryType)
: 'project';
}
function parseJsonPatchAfter(proposal: AutomationEvolutionProposal): Record<string, unknown> {
if (proposal.patch.format !== 'json') return {};
const after = proposal.patch.after;
if (typeof after !== 'string' || !after.trim()) return {};
try {
const parsed = JSON.parse(after);
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
} catch {
throw new Error('proposal patch.after is not valid JSON');
}
}
function withMemoryProvenance(body: string, proposal: AutomationEvolutionProposal): string {
const text = String(body ?? '').trimEnd();
const lines = text.split(/\r?\n/);
const hasProposal = lines.some((line) => /^Proposal:\s*/i.test(line));
const existingPackets = new Set(
lines
.map((line) => /^Source packet:\s*([A-Za-z0-9_-]+)\s*$/i.exec(line)?.[1])
.filter((id): id is string => Boolean(id)),
);
const provenance: string[] = [];
for (const packetId of proposal.sourcePacketIds ?? []) {
if (!existingPackets.has(packetId)) provenance.push(`Source packet: ${packetId}`);
}
if (!hasProposal) provenance.push(`Proposal: ${proposal.id}`);
if (provenance.length === 0) return text;View on GitHub (pinned to 5be4028344)
Solutions
- Open the proposal record and validate patch.after with a JSON parser; fix the syntax at the reported position.
- If the after payload is irreparable, reject the proposal and create a new one with a correctly serialized patch.
- When building json-format proposals programmatically, always use JSON.stringify rather than template literals.
Example fix
// before — hand-built JSON
patch: { format: 'json', after: '{ name: "x", }' }
// after — JSON.stringify
patch: { format: 'json', after: JSON.stringify({ name: 'x' }) } Defensive patterns
Strategy: validation
Validate before calling
if (proposal.patch.format === 'json') {
JSON.parse(proposal.patch.after); // throws if invalid
} Type guard
function isValidJsonAfter(after: unknown): boolean {
if (typeof after !== 'string') return false;
try { JSON.parse(after); return true; } catch { return false; }
} Try / catch
try {
await applyAutomationProposal(dataDir, id);
} catch (e) {
if (/patch\.after is not valid JSON/.test((e as Error).message)) {
// reject the proposal and recreate with a JSON.stringify'd patch
}
} Prevention
- Always build json-format patch.after with JSON.stringify.
- Validate patch.after parses before persisting the proposal.
- Avoid hand-editing proposals.json after creation.
When it happens
Trigger: Applying a memory-node or automation-template proposal whose patch.format is 'json' but patch.after contains malformed JSON (trailing comma, unquoted key, truncated string). Also triggered if after was hand-edited after proposal creation.
Common situations: Agent emitted patch.after with a trailing comma or single quotes; serialized JSON was truncated in storage; manual edit of proposals.json broke the after payload; template upsert payload was built by string concatenation.
Related errors
- proposal body is required
- proposal title is required
- proposal summary is required
- proposal patch is required
- proposal ${proposal.id} is ${proposal.status}, not reviewabl
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/44344e3497cf0c9f.
Report an issue: GitHub.