nexu-io/open-design · error
automation proposal not found
Error message
automation proposal not found
What it means
Thrown by updateProposalStatus() when the given id is not present in the persisted proposals list. updateProposalStatus is called internally after apply/reject handlers have already fetched the proposal, so reaching this branch usually means the proposal was removed between the get and the update (a race or concurrent delete).
Source
Thrown at apps/daemon/src/automation-proposals.ts:286
}
const patch = parseJsonPatchAfter(proposal);
const template = await upsertUserAutomationTemplate(dataDir, patch);
return {
automationTemplateId: template.id,
action: proposal.action,
path: `automation-templates/${template.id}.json`,
};
}
async function updateProposalStatus(
dataDir: string,
id: string,
status: AutomationProposalStatus,
metadataPatch: Record<string, unknown> = {},
): Promise<AutomationEvolutionProposal> {
const proposals = await listAutomationProposals(dataDir, { status: 'all' });
const index = proposals.findIndex((proposal) => proposal.id === id);
if (index < 0) throw new Error('automation proposal not found');
const current = proposals[index]!;
const next: AutomationEvolutionProposal = {
...current,
status,
updatedAt: new Date().toISOString(),
};
if (Object.keys(metadataPatch).length > 0) {
const currentMetadata =
current.metadata && typeof current.metadata === 'object' && !Array.isArray(current.metadata)
? current.metadata as Record<string, unknown>
: {};
next.metadata = {
...currentMetadata,
...metadataPatch,
} as NonNullable<AutomationEvolutionProposal['metadata']>;
} else if (current.metadata !== undefined) {
next.metadata = current.metadata as NonNullable<AutomationEvolutionProposal['metadata']>;
}View on GitHub (pinned to 5be4028344)
Solutions
- Re-fetch the proposal and confirm it still exists before retrying.
- Serialize reviewer actions through a single queue to avoid the get/update race.
- Avoid editing proposals.json out-of-band while the daemon is serving apply/reject requests.
Defensive patterns
Strategy: try-catch
Validate before calling
const proposals = await listAutomationProposals(dataDir, { status: 'all' });
if (!proposals.some((p) => p.id === id)) {
throw new Error('automation proposal not found');
} Try / catch
try {
await updateProposalStatus(dataDir, id, status, patch);
} catch (e) {
if ((e as Error).message === 'automation proposal not found') {
// re-fetch; the proposal was removed mid-flight — do not retry blindly
}
} Prevention
- Serialize reviewer actions to avoid the get/update race.
- Do not edit proposals.json out-of-band while the daemon is running.
- Re-fetch before retrying any 'not found' from the status-update path.
When it happens
Trigger: Two concurrent callers: one applies/rejects and the other deletes the proposal, then the first caller's updateProposalStatus runs and cannot find the id. Also reachable if the proposals.json file was edited externally and the id no longer exists.
Common situations: Concurrent reviewer actions; external script rewriting proposals.json; proposal expired/rotated between fetch and status update; test reset that wiped the store mid-flow.
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/e8d25e77401bbec3.
Report an issue: GitHub.