nexu-io/open-design · error · Error
The host returned an invalid confirmation.
Error message
The host returned an invalid confirmation.
What it means
Browser-side error thrown during form submission when callConfirm() returned a result that does not contain a `briefConfirmationId`. The confirm_brief tool is expected to return structuredContent (or a nested result.result.structuredContent) with at least `briefConfirmationId`; absence means the host/daemon returned a malformed or incomplete confirmation payload. This is treated as a confirmation failure and the form reverts to the ready phase with an error message.
Source
Thrown at apps/daemon/src/mcp-apps/brief-resource.ts:467
status.textContent = copy().contextFailed;
scheduleSizeChanged();
});
});
form.addEventListener("submit", async (event) => {
event.preventDefault();
if (phase !== "ready" || !draft || !form.reportValidity()) return;
applyPhase("confirming");
let payload;
try {
const result = await callConfirm({
briefDraftId: draft.briefDraftId,
nonce: draft.nonce,
answers: selections(),
locale: effectiveLocale(),
});
payload = result && (result.structuredContent || (result.result && result.result.structuredContent) || result);
if (!payload || !payload.briefConfirmationId) throw new Error(copy().invalidConfirmation);
} catch (error) {
applyPhase(
"ready",
publicErrorMessage(error, copy().confirmFailed),
);
return;
}
confirmedPayload = payload;
applyPhase("confirmed_publishing");
try {
const contextCleared = await publishConfirmation(confirmedPayload);
applyPhase(
"delivered",
contextCleared ? undefined : copy().deliveredContextCleanupFailed,
);
} catch {
applyPhase("confirmed_publish_failed");
}View on GitHub (pinned to 5be4028344)
Solutions
- Check the Open Design daemon logs for the confirm_brief call — a validation error there is the usual cause.
- Ensure the daemon is the version that matches the brief UI (both should ship together).
- Re-open the brief to get a fresh briefDraftId/nonce (the draft may have expired) and confirm again.
- If you control the host, verify it forwards structuredContent from the tools/call result unchanged.
Defensive patterns
Strategy: validation
Validate before calling
// Validate the confirm_brief tool result shape before treating it as a confirmed payload.
function isBriefConfirmation(result: unknown): result is { briefConfirmationId: string } {
if (!result || typeof result !== 'object') return false;
const r = result as Record<string, unknown>;
const payload = r.structuredContent ?? (r.result && r.result.structuredContent) ?? r;
return !!payload && typeof payload.briefConfirmationId === 'string';
}
const result = await callConfirm(args);
if (!isBriefConfirmation(result)) {
// Show a user-facing error; do not advance to publish.
} Type guard
function isBriefConfirmationPayload(value: unknown): value is { briefConfirmationId: string; summary?: string } {
if (!value || typeof value !== 'object') return false;
const v = value as Record<string, unknown>;
return typeof v.briefConfirmationId === 'string';
} Try / catch
// In-source: the submit handler already wraps callConfirm in try/catch and re-applies 'ready'.
try {
const result = await callConfirm(args);
payload = result && (result.structuredContent || (result.result && result.result.structuredContent) || result);
if (!payload || !payload.briefConfirmationId) throw new Error(copy().invalidConfirmation);
} catch (error) {
applyPhase('ready', publicErrorMessage(error, copy().confirmFailed));
return;
} Prevention
- Keep the brief UI and the daemon in lockstep so confirm_brief always returns briefConfirmationId.
- Re-open the brief to refresh briefDraftId/nonce if confirmation keeps failing (the draft may have expired).
- If you proxy tool results, forward structuredContent unchanged.
When it happens
Trigger: The confirm_brief tool returned `{}` or a result without structuredContent; the daemon's confirm_brief handler errored partway and returned a partial payload; a nonce/briefDraftId mismatch caused the daemon to reject without setting briefConfirmationId; the host rewrote the result envelope and dropped structuredContent.
Common situations: Daemon version skew (older daemon that does not mint briefConfirmationId); the brief draft expired before confirmation (nonce no longer valid); a proxy/host stripped structuredContent from the tool result; bug in the confirm_brief handler returning early.
Related errors
- The MCP Apps bridge is unavailable.
- The host cannot publish the confirmed brief.
- pluginWorkflowId requires a validated externalPluginContext
- The Open Design brief is incomplete. Missing: ${missing.join
- 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/ec62481fa8337e98.
Report an issue: GitHub.