{"record":{"id":"3226ed707e2a0d87","repo":"vercel/ai","slug":"invalid-ai-sdk-harness-skills-manifest-manifest","errorCode":null,"errorMessage":"Invalid AI SDK harness skills manifest: ${manifestPath}","messagePattern":"Invalid AI SDK harness skills manifest: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness/src/utils/write-skills.ts","lineNumber":303,"sourceCode":"}\n\nasync function readSkillsManifest({\n  sandbox,\n  manifestPath,\n  abortSignal,\n}: {\n  sandbox: Experimental_SandboxSession;\n  manifestPath: string;\n  abortSignal?: AbortSignal;\n}): Promise<SkillsManifest | undefined> {\n  const content = await sandbox.readTextFile({\n    path: manifestPath,\n    abortSignal,\n  });\n  if (content == null) return undefined;\n  const parsed = await safeParseJSON({ text: content });\n  if (!parsed.success || !isSkillsManifest(parsed.value)) {\n    throw new Error(`Invalid AI SDK harness skills manifest: ${manifestPath}`);\n  }\n  return parsed.value;\n}\n\nfunction isSkillsManifest(value: unknown): value is SkillsManifest {\n  if (value == null || typeof value !== 'object' || Array.isArray(value)) {\n    return false;\n  }\n  const manifest = value as Record<string, unknown>;\n  if (\n    manifest.version !== SKILLS_MANIFEST_VERSION ||\n    (manifest.state !== 'complete' && manifest.state !== 'pending') ||\n    !Array.isArray(manifest.skills)\n  ) {\n    return false;\n  }\n  const names = new Set<string>();\n  for (const entry of manifest.skills) {","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness/src/utils/write-skills.ts#L285-L321","documentation":"Thrown by readSkillsManifest in packages/harness/src/utils/write-skills.ts when the skills manifest file (.ai-sdk-harness-skills.json) exists in the sandbox but is not valid JSON or fails the SkillsManifest structure/type check (version, state, skills entries). The harness uses this manifest to track which skills it manages, so a corrupt manifest is treated as a hard failure rather than silently ignored.","triggerScenarios":"Calling writeSkills where rootDir contains a .ai-sdk-harness-skills.json that was hand-edited, truncated by a previous crash, written by an incompatible SDK version, or is valid JSON but missing required fields (version 1, state 'complete'|'pending', skills array of {name, hash}).","commonSituations":"Manual editing of the manifest file; interrupted writes from an older version; another tool overwriting the file; version mismatch after upgrading the AI SDK harness.","solutions":["Delete or restore the corrupt .ai-sdk-harness-skills.json in rootDir, then re-run writeSkills (directories not owned by the harness may need manual cleanup).","Let the harness regenerate the manifest instead of editing it by hand.","Verify the manifest matches { version: 1, state: 'complete'|'pending', skills: [{ name, hash }] }.","Check for concurrent processes writing skills to the same rootDir simultaneously."],"exampleFix":"// before (rootDir/.ai-sdk-harness-skills.json, hand-edited)\n{ \"version\": 2, \"skills\": \"all of them\" }\n// after — delete the corrupt manifest and re-run\nawait sandbox.run({ command: 'rm /workspace/.ai-sdk-harness-skills.json' });\nawait writeSkills({ sandbox, rootDir, skills });","handlingStrategy":"try-catch","validationCode":"// Validate the manifest before the harness reads it\nconst raw = await sandbox.run({ command: 'cat ' + rootDir + '/.ai-sdk-harness-skills.json' });\nif (raw.exitCode === 0) {\n  const ok = (() => { try { const m = JSON.parse(raw.stdout); return m.version === 1 && ['complete','pending'].includes(m.state) && Array.isArray(m.skills); } catch { return false; } })();\n  if (!ok) await sandbox.run({ command: 'rm ' + rootDir + '/.ai-sdk-harness-skills.json' });\n}","typeGuard":"function isSkillsManifest(v: unknown): v is { version: 1; state: 'complete'|'pending'; skills: { name: string; hash: string }[] } {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) &&\n    (v as any).version === 1 &&\n    ['complete','pending'].includes((v as any).state) &&\n    Array.isArray((v as any).skills);\n}","tryCatchPattern":"try {\n  await writeSkills({ sandbox, rootDir, skills });\n} catch (error) {\n  if (/Invalid AI SDK harness skills manifest/.test((error as Error).message)) {\n    await sandbox.run({ command: `rm -f ${rootDir}/.ai-sdk-harness-skills.json` });\n    await writeSkills({ sandbox, rootDir, skills }); // regenerate\n  } else throw error;\n}","preventionTips":["Never hand-edit .ai-sdk-harness-skills.json.","Avoid concurrent writeSkills calls against the same rootDir.","After upgrading the AI SDK, remove stale manifests generated by older versions.","Keep manifest writes inside the harness so they stay atomic/consistent."],"tags":["sandbox","manifest","json","validation","harness"],"backgroundTag":"schema-validation-failed","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}