nexu-io/open-design · error · Error
--image not found: ${rel}
Error message
--image not found: ${rel} What it means
Thrown by resolveProjectImage when fs.stat fails on the resolved absolute path — the file does not exist (or is inaccessible due to permissions) inside the project directory. This runs after the path-traversal guard, so the path is known to be under the project root; the failure is purely existence/permissions.
Source
Thrown at apps/daemon/src/media/index.ts:234
* daemon to upload `/etc/passwd` to a paid model.
*/
async function resolveProjectImage(rel: unknown, projectDir: string): Promise<ImageRef | null> {
if (typeof rel !== 'string' || !rel.trim()) return null;
const projectRootResolved = path.resolve(projectDir);
const abs = path.resolve(projectRootResolved, rel.trim());
if (
abs !== projectRootResolved &&
!abs.startsWith(projectRootResolved + path.sep)
) {
throw new Error(
`--image path "${rel}" resolves outside the project directory.`,
);
}
let info;
try {
info = await stat(abs);
} catch {
throw new Error(`--image not found: ${rel}`);
}
if (!info.isFile()) {
throw new Error(`--image is not a regular file: ${rel}`);
}
// Cap at 16 MB. Beyond this, base64 inflation alone (≈4/3) starts
// hitting body-size limits at the upstream APIs and our own express
// 4mb body cap on inbound requests; bigger payloads should travel
// via the dedicated upload endpoint, not the dispatcher.
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.View on GitHub (pinned to 5be4028344)
Solutions
- Verify the file exists at the exact relative path under the project root before the media call.
- Check filename case and extension spelling on case-sensitive filesystems.
- Generate or copy the image into the project assets folder first.
- Confirm fs permissions allow the daemon process to read the path.
Example fix
// before: file not written yet
--image assets/render.png
// after: ensure it exists
await renderAndSave("assets/render.png");
--image assets/render.png Defensive patterns
Strategy: validation
Validate before calling
import { access } from 'node:fs/promises';
try { await access(abs); } catch { throw new Error(`--image not found: ${rel}`); } Prevention
- Confirm the file exists at the exact path before the media call.
- Watch for case sensitivity on Linux/macOS filesystems.
- Generate assets before referencing them.
When it happens
Trigger: Typo in the image filename; file not yet saved/generated when the media call runs; wrong project directory; permission denied on the file or a parent dir; case-sensitivity mismatch on case-sensitive filesystems.
Common situations: Agent references an image it has not yet written; race between generation and consumption; cross-platform path casing (PNG vs png); file deleted between listing and fetch.
Related errors
- --image is not a regular file: ${rel}
- project not found: ${projectId}
- --image path "${rel}" resolves outside the project directory
- --image too large (${info.size} bytes; max ${MAX_IMAGE_BYTES
- --image has unsupported extension "${ext}". Use png, jpg, jp
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/68c19626f7a698ed.
Report an issue: GitHub.