{"record":{"id":"fedaabe4e2d856ad","repo":"nexu-io/open-design","slug":"artifact-manifest-invalid","errorCode":"ARTIFACT_MANIFEST_INVALID","errorMessage":"invalid artifactManifest: ${message}","messagePattern":"invalid artifactManifest: (.+?)","errorType":"error_code","errorClass":"ArtifactManifestInvalidError","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/artifacts/create.ts","lineNumber":62,"sourceCode":"export function buildCreateArtifactRequestBody(input: CreateProjectArtifactInput): JsonObject {\n  return {\n    name: input.name,\n    content: input.content,\n    encoding: input.encoding === 'base64' ? 'base64' : 'utf8',\n    artifact: true,\n    overwrite: false,\n    ...(input.artifactManifest === undefined ? {} : { artifactManifest: input.artifactManifest }),\n  };\n}\n\nexport function resolveCreateArtifactManifest(input: CreateProjectArtifactInput): unknown {\n  const manifest = input.artifactManifest !== undefined && input.artifactManifest !== null\n    ? input.artifactManifest\n    : inferLegacyManifest(input.name);\n  if (manifest) {\n    const validated = validateArtifactManifestInput(manifest, input.name);\n    if (!validated.ok) {\n      throw new ArtifactManifestInvalidError(validated.error);\n    }\n    return validated.value;\n  }\n  throw new ArtifactManifestRequiredError(input.name);\n}\n\nexport async function createProjectArtifactFile(options: CreateProjectArtifactOptions): Promise<unknown> {\n  const { input } = options;\n  const body = input.encoding === 'base64'\n    ? Buffer.from(input.content, 'base64')\n    : Buffer.from(input.content, 'utf8');\n  return await options.writeProjectFile(\n    options.projectsRoot,\n    options.projectId,\n    input.name,\n    body,\n    {\n      overwrite: false,","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/artifacts/create.ts#L44-L80","documentation":"Thrown by resolveCreateArtifactManifest() as ArtifactManifestInvalidError (code ARTIFACT_MANIFEST_INVALID) when an explicit artifactManifest was supplied but validateArtifactManifestInput() rejected it. The appended message is the specific validation failure (kind/renderer/exports/status/path constraint). The validation rules live in artifacts/manifest.ts.","triggerScenarios":"POST /api/projects/:id/files with artifact=true and an artifactManifest whose kind is not in ALLOWED_KINDS, renderer not in ALLOWED_RENDERERS, exports empty or containing unsupported values, status not streaming|complete|error, supportingFiles with absolute/traversal paths, or metadata exceeding 16KB. Also fired by `od artifacts create --manifest` because it forwards to the same resolver.","commonSituations":"Mismatched kind/renderer pair (e.g. kind 'deck' with renderer 'html' instead of 'deck-html'); forgetting exports is required and non-empty; passing a relative path that contains '..' in supportingFiles; metadata blob grown past 16KB.","solutions":["Read the appended message — it names the exact field that failed (e.g. 'artifactManifest.renderer is not allowed').","Cross-check kind against ALLOWED_KINDS and renderer against ALLOWED_RENDERERS in apps/daemon/src/artifacts/manifest.ts.","Ensure exports is a non-empty array of values from ALLOWED_EXPORTS (html, pdf, zip, jsx, md, svg, txt).","Strip absolute paths, '..' segments, and null bytes from supportingFiles; keep metadata under 16KB."],"exampleFix":"// before\n{\n  \"artifactManifest\": { \"kind\": \"pitch\", \"renderer\": \"slides\", \"exports\": [] }\n}\n// after — allowed kind/renderer, non-empty exports\n{\n  \"artifactManifest\": {\n    \"kind\": \"deck\",\n    \"renderer\": \"deck-html\",\n    \"exports\": [\"html\", \"pdf\", \"zip\"]\n  }\n}","handlingStrategy":"validation","validationCode":"import { validateArtifactManifestInput } from './artifacts/manifest.js';\nconst result = validateArtifactManifestInput(manifest, name);\nif (!result.ok) throw new Error(`manifest invalid: ${result.error}`);\n// safe to submit\npostCreateArtifactRequest({ baseUrl, projectId, input: { name, content, encoding, artifactManifest: result.value } });","typeGuard":"const ALLOWED_KINDS = new Set(['html','deck','react-component','markdown-document','svg','diagram','code-snippet','mini-app','design-system']);\nconst ALLOWED_RENDERERS = new Set(['html','deck-html','react-component','markdown','svg','diagram','code','mini-app','design-system']);\nconst ALLOWED_EXPORTS = new Set(['html','pdf','zip','jsx','md','svg','txt']);\nfunction isValidManifestShape(m: unknown): m is { kind: string; renderer: string; exports: string[] } {\n  return !!m && typeof m === 'object'\n    && typeof (m as any).kind === 'string' && ALLOWED_KINDS.has((m as any).kind)\n    && typeof (m as any).renderer === 'string' && ALLOWED_RENDERERS.has((m as any).renderer)\n    && Array.isArray((m as any).exports) && (m as any).exports.every((e: string) => ALLOWED_EXPORTS.has(e));\n}","tryCatchPattern":"try {\n  await createProjectArtifactFile(options);\n} catch (e) {\n  if (e instanceof ArtifactManifestInvalidError) {\n    // surface validated.error to the user; do not retry unchanged\n  }\n  throw e;\n}","preventionTips":["Validate the manifest with validateArtifactManifestInput before submitting.","Keep kind/renderer as a matched pair (deck -> deck-html, html -> html).","Always supply a non-empty exports array.","Keep supportingFiles relative and free of '..' segments."],"tags":["artifacts","manifest","validation","api","daemon"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}