nexu-io/open-design · error · Error
--image has unsupported extension "${ext}". Use png, jpg, jp
Error message
--image has unsupported extension "${ext}". Use png, jpg, jpeg, webp, or gif. What it means
Thrown by resolveProjectImage after reading the file: the extension is not in the tight allowlist of png, jpg, jpeg, webp, gif. The allowlist matches exactly what i2v / image-edit upstream endpoints consume, preventing arbitrary content from being smuggled through as a data URL.
Source
Thrown at apps/daemon/src/media/index.ts:261
const MAX_IMAGE_BYTES = 16 * 1024 * 1024;
if (info.size > MAX_IMAGE_BYTES) {
throw new Error(
`--image too large (${info.size} bytes; max ${MAX_IMAGE_BYTES}).`,
);
}
const bytes = await readFile(abs);
const ext = path.extname(abs).toLowerCase();
// Tight allowlist: only what i2v / image-edit endpoints actually
// consume. Avoids smuggling arbitrary content through as data URLs.
const mime = ({
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.webp': 'image/webp',
'.gif': 'image/gif',
})[ext];
if (!mime) {
throw new Error(
`--image has unsupported extension "${ext}". Use png, jpg, jpeg, webp, or gif.`,
);
}
return {
path: rel.trim(),
abs,
mime,
size: bytes.length,
dataUrl: `data:${mime};base64,${bytes.toString('base64')}`,
};
}
function clampNumber(value: unknown, allowed: number[]): number | undefined {
// Accept exact registry values; otherwise snap to the nearest allowed
// bucket so a hallucinated `Number.MAX_SAFE_INTEGER` can't bill an
// entire month of credits when real providers plug in.
if (typeof value !== 'number' || !Number.isFinite(value)) return undefined;
if (allowed.length === 0) return undefined;View on GitHub (pinned to 5be4028344)
Solutions
- Convert the image to png, jpg/jpeg, webp, or gif before referencing it.
- Rename the file to a correct, supported extension that matches its actual content.
- For SVGs, rasterize to PNG at the target size first.
- For HEIC/AVIF, transcode with an image library to JPEG/WebP.
Example fix
// before
--image assets/logo.svg
// after
sharp('assets/logo.svg').png().toFile('assets/logo.png')
--image assets/logo.png Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
if (!ALLOWED.has(path.extname(abs).toLowerCase())) throw new Error('unsupported image extension'); Type guard
function isAllowedImageExt(abs: string): boolean {
return new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']).has(path.extname(abs).toLowerCase());
} Prevention
- Convert SVG/HEIC/AVIF/BMP to PNG/JPEG/WebP/GIF first.
- Ensure file extensions match actual content.
- Validate extension in the UI before submit.
When it happens
Trigger: Passing `--image assets/icon.svg`, `.bmp`, `.tiff`, `.heic`, `.avif`, or an extensionless/renamed file; a file whose extension casing differs (handled by lowercasing, so only genuinely unsupported extensions fail).
Common situations: SVG icons (not bitmap-supported); modern formats (AVIF/HEIC) not yet allowlisted; extensionless exports; renamed files where the extension no longer reflects content.
Related errors
- --image path "${rel}" resolves outside the project directory
- --image not found: ${rel}
- --image is not a regular file: ${rel}
- --image too large (${info.size} bytes; max ${MAX_IMAGE_BYTES
- unsupported surface: ${surface}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/4e1c39013b0cee73.
Report an issue: GitHub.