{"record":{"id":"20dda727d0cede0b","repo":"vercel/ai","slug":"no-image-generated","errorCode":null,"errorMessage":"No image generated.","messagePattern":"No image generated\\.","errorType":"exception","errorClass":"NoImageGeneratedError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/generate-image/generate-image.ts","lineNumber":298,"sourceCode":"          ).images;\n          if (Array.isArray(imagesValue) && imagesValue.length === 0) {\n            delete (providerMetadata[providerName] as { images?: unknown })\n              .images;\n          }\n        } else {\n          providerMetadata[providerName] ??= { images: [] };\n          providerMetadata[providerName].images.push(...metadata.images);\n        }\n      }\n    }\n\n    responses.push(result.response);\n  }\n\n  logWarnings({ warnings, provider: model.provider, model: model.modelId });\n\n  if (!images.length) {\n    throw new NoImageGeneratedError({ responses });\n  }\n\n  return new DefaultGenerateImageResult({\n    images,\n    calls,\n    warnings,\n    responses,\n    providerMetadata,\n    usage: totalUsage,\n  });\n}\n\nclass DefaultGenerateImageResult implements GenerateImageResult {\n  readonly images: Array<GeneratedFile>;\n  readonly calls: Array<GenerateImageCall>;\n  readonly warnings: Array<Warning>;\n  readonly responses: Array<ImageModelResponseMetadata>;\n  readonly providerMetadata: ImageModelV4ProviderMetadata;","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/generate-image/generate-image.ts#L280-L316","documentation":"Each skill's SKILL.md is written by the harness from the skill definition itself, so an attached file resolving to path 'SKILL.md' (after normalization) would collide with it. validateACPSkills rejects any attached file whose normalized path equals 'SKILL.md'.","triggerScenarios":"Including a file with path 'SKILL.md', './SKILL.md', or an equivalent in a skill's `files` array when the skill definition already supplies its own SKILL.md content.","commonSituations":"Bulk-copying an existing skills directory (which contains SKILL.md files) into the files array; templated loaders that attach every file in a folder, including SKILL.md; hand-writing extra docs and accidentally naming them SKILL.md.","solutions":["Remove the attached SKILL.md entry and put its content in the skill's definition (the skill body/definition field) instead.","Rename the extra document to something else, e.g. 'docs/NOTES.md'.","Filter out any file whose normalized path is 'SKILL.md' before building the files array."],"exampleFix":"// before\n{ name: 'reviewer', definition: '...', files: [{ path: 'SKILL.md', data }] }\n\n// after\n{ name: 'reviewer', definition: data, files: [] }","handlingStrategy":"validation","validationCode":"import path from 'node:path';\nconst RESERVED = new Set(['SKILL.md', './SKILL.md'].map(p => path.posix.normalize(p)));\nskills.forEach(s => {\n  s.files = (s.files ?? []).filter(f => !RESERVED.has(path.posix.normalize(f.path)));\n});","typeGuard":"function isReservedSkillFile(p: string): boolean {\n  return path.posix.normalize(p) === 'SKILL.md';\n}","tryCatchPattern":"try {\n  await agent.addSkills(skills);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('SKILL.md is reserved')) {\n    await agent.addSkills(skills.map(s => ({\n      ...s,\n      files: (s.files ?? []).filter(f => !isReservedSkillFile(f.path)),\n    })));\n  } else {\n    throw error;\n  }\n}","preventionTips":["Never include SKILL.md in a skill's files array; the definition supplies it.","When scanning existing skill directories, exclude SKILL.md from attached files.","Put supplementary docs under subpaths like 'docs/' instead of the skill root."],"tags":["acp","validation","reserved-path","skills"],"backgroundTag":"reserved-path-conflict","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}