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

  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.