nexu-io/open-design · warning · Error
The Open Design brief nonce is invalid.
Error message
The Open Design brief nonce is invalid.
What it means
Thrown when the nonce supplied to confirm_brief does not match the nonce stored on the draft for that briefDraftId. The nonce is a 24-byte random hex generated per draft by collect_brief and acts as a possession token binding the confirm to the exact draft that created it. A mismatch means the (briefDraftId, nonce) pair did not come from the same collect_brief call.
Source
Thrown at apps/daemon/src/mcp-brief.ts:443
};
},
confirm(input) {
const at = now();
pruneExpired(at);
const briefDraftId = readRequiredString(
input.briefDraftId,
'briefDraftId',
);
const nonce = readRequiredString(input.nonce, 'nonce');
const draft = drafts.get(briefDraftId);
if (!draft) {
throw new Error(
'The Open Design brief has expired or is unknown. Call collect_brief again.',
);
}
if (draft.nonce !== nonce) {
throw new Error('The Open Design brief nonce is invalid.');
}
const submittedAnswers = readAnswerRecord(input.answers, 'answers');
const mergedAnswers: UnknownRecord = {
...draft.knownAnswers,
...submittedAnswers,
};
const decision = collectOpenDesignBrief({
artifactType: draft.artifactType,
knownAnswers: mergedAnswers,
});
if (!decision.complete) {
const missing = decision.questions.map((question) => question.id);
throw new Error(
`The Open Design brief is incomplete. Missing: ${missing.join(', ')}.`,
);
}
const confirmationAnswersDigest = stableAnswerDigest(decision.answers);
if (draft.confirmation) {View on GitHub (pinned to 5be4028344)
Solutions
- Use the nonce returned by the exact collect_brief response that produced briefDraftId.
- Pass briefDraftId and nonce together from the collect_brief result without transforming them.
Example fix
// before
confirm_brief({ briefDraftId, nonce: oldNonce, answers }) // throws [361]
// after
const { briefDraftId, nonce } = await collect_brief({ artifactType: 'website' })
await confirm_brief({ briefDraftId, nonce, answers }) Defensive patterns
Strategy: retry
Type guard
function isBriefDraftPair(x: unknown): x is { briefDraftId: string; nonce: string } {
return !!x
&& typeof (x as any)?.briefDraftId === 'string'
&& typeof (x as any)?.nonce === 'string'
&& /^[0-9a-f]{48}$/i.test((x as any).nonce);
} Try / catch
try {
await confirmBrief(input);
} catch (e) {
if (/nonce is invalid/i.test(e.message)) {
const fresh = await collectBrief({ artifactType });
return confirmBrief({ ...input, briefDraftId: fresh.briefDraftId, nonce: fresh.nonce });
}
throw e;
} Prevention
- Treat briefDraftId and nonce as one atomic token returned by collect_brief.
- Never split or cache them independently.
- Pass both straight through without transformation.
When it happens
Trigger: Pairing a briefDraftId from one collect_brief call with a nonce from another; replaying an old nonce after re-collecting; truncating or mutating the nonce string.
Common situations: An agent cached the nonce separately from briefDraftId and they desynced; copy-pasting only one of the two fields.
Related errors
- pluginWorkflowId requires a validated externalPluginContext
- The Open Design brief has expired or is unknown. Call collec
- The Open Design brief is incomplete. Missing: ${missing.join
- This Open Design brief was already confirmed with different
- artifactType must be one of: ${Object.keys(openDesignBriefCa
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/59bcb12dc5d99205.
Report an issue: GitHub.