{"record":{"id":"99e3e2ac11c77ec9","repo":"nexu-io/open-design","slug":"artifactmanifest-must-be-an-object","errorCode":null,"errorMessage":"artifactManifest must be an object","messagePattern":"artifactManifest must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/mcp.ts","lineNumber":2854,"sourceCode":"    return null;\n  }\n}\n\nasync function createArtifact(\n  baseUrl: string,\n  args: McpArgs,\n  headers?: Record<string, string>,\n) {\n  const { id, resolved, active } = await resolveProjectArg(baseUrl, args.project, headers);\n  requireString(args.name, 'name');\n  requireString(args.content, 'content');\n  if (\n    args.artifactManifest !== undefined &&\n    (args.artifactManifest === null ||\n      typeof args.artifactManifest !== 'object' ||\n      Array.isArray(args.artifactManifest))\n  ) {\n    throw new Error('artifactManifest must be an object');\n  }\n  const artifactManifest =\n    args.artifactManifest\n      ? args.artifactManifest\n      : undefined;\n  const payload = await postCreateArtifactRequest({\n    baseUrl,\n    projectId: id,\n    ...(headers ? { headers } : {}),\n    input: {\n      name: args.name,\n      content: args.content,\n      encoding: args.encoding === 'base64' ? 'base64' : 'utf8',\n      ...(artifactManifest === undefined ? {} : { artifactManifest }),\n    },\n  });\n  const result = payload && typeof payload === 'object' && !Array.isArray(payload)\n    ? (payload as JsonObject)","sourceCodeStart":2836,"sourceCodeEnd":2872,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/mcp.ts#L2836-L2872","documentation":"Thrown in the artifact-creation MCP handler before POSTing to the artifact endpoint. `artifactManifest` is an optional structured manifest describing the artifact; if present it must be a plain object so downstream artifact rendering can read named fields. null, arrays, and primitives are rejected to fail fast rather than produce a malformed artifact.","triggerScenarios":"Calling the create-artifact MCP tool with `artifactManifest: [...]`, `artifactManifest: null`, or a JSON string. Happens when a caller copies a list of manifest entries into the field, or forwards a serialized manifest without parsing.","commonSituations":"LLM emits an array because the schema is ambiguous; manifest imported from a JSON file left as a string; passing the whole request envelope instead of its manifest sub-object.","solutions":["Pass a plain object: `artifactManifest: { ... }`, or omit it to use the default.","Parse serialized manifests: `artifactManifest: JSON.parse(raw)`.","If you built an array of manifests, pick the single intended entry.","Verify the manifest field names against the artifact route's expected schema in packages/contracts."],"exampleFix":"// before\n{ name, content, artifactManifest: null }\n// after\n{ name, content }  // omit when default manifest is fine","handlingStrategy":"type-guard","validationCode":"if (artifactManifest !== undefined && (artifactManifest === null || typeof artifactManifest !== 'object' || Array.isArray(artifactManifest))) {\n  throw new TypeError('artifactManifest must be a plain object');\n}","typeGuard":"function isManifest(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Omit artifactManifest when the default is acceptable.","Parse serialized manifests before submission.","Validate the manifest against the artifact route's contract schema."],"tags":["mcp","validation","artifacts","input-coercion"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}