{"record":{"id":"bf38b28f9bddedcc","repo":"nexu-io/open-design","slug":"field-must-be-an-object","errorCode":null,"errorMessage":"${field} must be an object","messagePattern":"(.+?) must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/daemon/src/mcp-brief.ts","lineNumber":655,"sourceCode":"  if (value === undefined) return 'Untitled Open Design artifact';\n  const title = readRequiredString(value, 'projectTitle').trim();\n  if (title.length > 256) {\n    throw new Error('projectTitle must be at most 256 characters');\n  }\n  return title;\n}\n\nfunction readRequiredString(value: unknown, field: string): string {\n  if (typeof value !== 'string' || value.length === 0) {\n    throw new Error(`${field} must be a non-empty string`);\n  }\n  return value;\n}\n\nfunction readAnswerRecord(value: unknown, field: string): UnknownRecord {\n  if (value === undefined) return {};\n  if (!value || typeof value !== 'object' || Array.isArray(value)) {\n    throw new Error(`${field} must be an object`);\n  }\n  return { ...(value as UnknownRecord) };\n}\n\nfunction stableAnswerDigest(answers: OpenDesignBriefAnswers): string {\n  return JSON.stringify(\n    Object.entries(answers)\n      .sort(([left], [right]) => left.localeCompare(right))\n      .map(([key, value]) => [key, [...value]]),\n  );\n}\n","sourceCodeStart":637,"sourceCodeEnd":667,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/mcp-brief.ts#L637-L667","documentation":"readAnswerRecord guards the answers argument. undefined is allowed (defaults to {}); any other non-object, null, or array is rejected. answers must be a plain record mapping question id to option id.","triggerScenarios":"Passing answers as a JSON string; passing an array of the form [{ id, value }]; passing null.","commonSituations":"The caller JSON.stringify'd answers and forgot to parse; an LLM returned an array form.","solutions":["Pass answers as a plain object such as { 'website.goal': 'launch-product' }.","If you stringified it, parse it first.","Convert array forms to a record before calling."],"exampleFix":"// before\nconfirm_brief({ briefDraftId, nonce, answers: JSON.stringify(answers) })\n\n// after\nconfirm_brief({ briefDraftId, nonce, answers: { 'website.goal': 'launch-product' } })","handlingStrategy":"type-guard","validationCode":"if (answers != null && (typeof answers !== 'object' || Array.isArray(answers))) {\n  throw new Error('answers must be an object');\n}","typeGuard":"function isAnswerRecord(v: unknown): v is Record<string, string> {\n  return v == null || (typeof v === 'object' && !Array.isArray(v));\n}","tryCatchPattern":null,"preventionTips":["Keep answers as a record keyed by question id, never a stringified blob or array.","Parse any transported JSON before submit.","Convert LLM array forms to a record before calling confirm_brief."],"tags":["mcp","brief","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}