{"record":{"id":"68c19626f7a698ed","repo":"nexu-io/open-design","slug":"image-not-found-rel","errorCode":null,"errorMessage":"--image not found: ${rel}","messagePattern":"--image not found: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":234,"sourceCode":" * daemon to upload `/etc/passwd` to a paid model.\n */\nasync function resolveProjectImage(rel: unknown, projectDir: string): Promise<ImageRef | null> {\n  if (typeof rel !== 'string' || !rel.trim()) return null;\n  const projectRootResolved = path.resolve(projectDir);\n  const abs = path.resolve(projectRootResolved, rel.trim());\n  if (\n    abs !== projectRootResolved &&\n    !abs.startsWith(projectRootResolved + path.sep)\n  ) {\n    throw new Error(\n      `--image path \"${rel}\" resolves outside the project directory.`,\n    );\n  }\n  let info;\n  try {\n    info = await stat(abs);\n  } catch {\n    throw new Error(`--image not found: ${rel}`);\n  }\n  if (!info.isFile()) {\n    throw new Error(`--image is not a regular file: ${rel}`);\n  }\n  // Cap at 16 MB. Beyond this, base64 inflation alone (≈4/3) starts\n  // hitting body-size limits at the upstream APIs and our own express\n  // 4mb body cap on inbound requests; bigger payloads should travel\n  // via the dedicated upload endpoint, not the dispatcher.\n  const MAX_IMAGE_BYTES = 16 * 1024 * 1024;\n  if (info.size > MAX_IMAGE_BYTES) {\n    throw new Error(\n      `--image too large (${info.size} bytes; max ${MAX_IMAGE_BYTES}).`,\n    );\n  }\n  const bytes = await readFile(abs);\n  const ext = path.extname(abs).toLowerCase();\n  // Tight allowlist: only what i2v / image-edit endpoints actually\n  // consume. Avoids smuggling arbitrary content through as data URLs.","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L216-L252","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: file not written yet\n--image assets/render.png\n// after: ensure it exists\nawait renderAndSave(\"assets/render.png\");\n--image assets/render.png","handlingStrategy":"validation","validationCode":"import { access } from 'node:fs/promises';\ntry { await access(abs); } catch { throw new Error(`--image not found: ${rel}`); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["media","files","not-found","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}