nexu-io/open-design · error · Error

--image not found

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.

Solutions

  1. Verify the file exists at the exact relative path under the project root before the media call.
  2. Check filename case and extension spelling on case-sensitive filesystems.
  3. Generate or copy the image into the project assets folder first.
  4. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/68c19626f7a698ed. Report an issue: GitHub.

Appendix: 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)