vercel/ai · error · NoImageGeneratedError
No image generated.
Error message
No image generated.
What it means
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'.
Source
Thrown at packages/ai/src/generate-image/generate-image.ts:298
).images;
if (Array.isArray(imagesValue) && imagesValue.length === 0) {
delete (providerMetadata[providerName] as { images?: unknown })
.images;
}
} else {
providerMetadata[providerName] ??= { images: [] };
providerMetadata[providerName].images.push(...metadata.images);
}
}
}
responses.push(result.response);
}
logWarnings({ warnings, provider: model.provider, model: model.modelId });
if (!images.length) {
throw new NoImageGeneratedError({ responses });
}
return new DefaultGenerateImageResult({
images,
calls,
warnings,
responses,
providerMetadata,
usage: totalUsage,
});
}
class DefaultGenerateImageResult implements GenerateImageResult {
readonly images: Array<GeneratedFile>;
readonly calls: Array<GenerateImageCall>;
readonly warnings: Array<Warning>;
readonly responses: Array<ImageModelResponseMetadata>;
readonly providerMetadata: ImageModelV4ProviderMetadata;View on GitHub (pinned to 69428b1f8b)
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.
Example fix
// before
{ name: 'reviewer', definition: '...', files: [{ path: 'SKILL.md', data }] }
// after
{ name: 'reviewer', definition: data, files: [] } Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
const RESERVED = new Set(['SKILL.md', './SKILL.md'].map(p => path.posix.normalize(p)));
skills.forEach(s => {
s.files = (s.files ?? []).filter(f => !RESERVED.has(path.posix.normalize(f.path)));
}); Type guard
function isReservedSkillFile(p: string): boolean {
return path.posix.normalize(p) === 'SKILL.md';
} Try / catch
try {
await agent.addSkills(skills);
} catch (error) {
if (error instanceof Error && error.message.includes('SKILL.md is reserved')) {
await agent.addSkills(skills.map(s => ({
...s,
files: (s.files ?? []).filter(f => !isReservedSkillFile(f.path)),
})));
} else {
throw error;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- maxEmbeddingsPerCall must be greater than 0
- maxInputBytesPerCall must be greater than 0
- No object generated: the model did not return a response.
- ACP credentialEnv and credentialBrokering must be configured
- ACP credentialEnv and credentialBrokering must be configured
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/20dda727d0cede0b.
Report an issue: GitHub.