{"record":{"id":"743be0530131c3e7","repo":"nexu-io/open-design","slug":"artifacttype-must-be-one-of-object-keys-opendes","errorCode":null,"errorMessage":"artifactType must be one of: ${Object.keys(openDesignBriefCatalog).join(', ')}","messagePattern":"artifactType must be one of: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/daemon/src/mcp-brief.ts","lineNumber":510,"sourceCode":"          ? { externalPluginContext: draft.externalPluginContext }\n          : {}),\n      };\n      draft.confirmation = confirmation;\n      draft.confirmationAnswersDigest = confirmationAnswersDigest;\n      if (draft.pluginWorkflowId && draft.briefState !== 'skipped') {\n        draft.briefState = 'confirmed';\n      }\n      return confirmation;\n    },\n  };\n}\n\nfunction readArtifactType(value: unknown): OpenDesignBriefArtifactType {\n  if (\n    typeof value !== 'string'\n    || !Object.hasOwn(openDesignBriefCatalog, value)\n  ) {\n    throw new Error(\n      `artifactType must be one of: ${Object.keys(openDesignBriefCatalog).join(', ')}`,\n    );\n  }\n  return value as OpenDesignBriefArtifactType;\n}\n\nexport function readBriefLocale(value: unknown): LocalMcpBriefLocale {\n  if (typeof value !== 'string' || value.trim().length === 0) return 'en';\n  const normalized = value.trim().replaceAll('_', '-').toLowerCase();\n  if (normalized === 'zh' || normalized === 'zh-cn' || normalized.startsWith('zh-hans')) {\n    return 'zh-CN';\n  }\n  if (\n    normalized === 'zh-tw'\n    || normalized === 'zh-hk'\n    || normalized === 'zh-mo'\n    || normalized.startsWith('zh-hant')\n  ) {","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/mcp-brief.ts#L492-L528","documentation":"readArtifactType rejects any value that is not a string key of openDesignBriefCatalog (packages/contracts/src/api/brief.ts), which defines exactly eight artifact types such as website and product-prototype. The thrown message enumerates the accepted keys at runtime, so the canonical list is whatever the current catalog exposes.","triggerScenarios":"Passing artifactType as undefined, a number, an empty string, a typo, or a value from an older or newer version whose catalog keys differ.","commonSituations":"Version skew between the contracts package the caller hardcoded and the daemon's catalog; free-text artifactType supplied by an LLM.","solutions":["Pass one of the keys printed in the error message.","Source the allowed set from OPEN_DESIGN_BRIEF_ARTIFACT_TYPES in @open-design/contracts instead of hardcoding.","Align the contracts dependency version with the daemon."],"exampleFix":"// before\ncollect_brief({ artifactType: 'web-site' }) // typo -> throws [364]\n\n// after\ncollect_brief({ artifactType: 'website' })","handlingStrategy":"type-guard","validationCode":"import { OPEN_DESIGN_BRIEF_ARTIFACT_TYPES } from '@open-design/contracts';\nconst valid = new Set(OPEN_DESIGN_BRIEF_ARTIFACT_TYPES);\nif (!valid.has(artifactType)) {\n  throw new Error(`artifactType must be one of: ${[...valid].join(', ')}`);\n}","typeGuard":"import { OPEN_DESIGN_BRIEF_ARTIFACT_TYPES } from '@open-design/contracts';\nfunction isArtifactType(v: unknown): v is OpenDesignBriefArtifactType {\n  return typeof v === 'string' && OPEN_DESIGN_BRIEF_ARTIFACT_TYPES.includes(v as any);\n}","tryCatchPattern":null,"preventionTips":["Never hardcode the artifactType list; import it from @open-design/contracts.","Validate before calling collect_brief.","Keep the contracts dependency version aligned with the daemon."],"tags":["mcp","brief","validation","enum"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}