musistudio/claude-code-router · error · Error

Unsupported input image format: ${resolved}

Error message

Unsupported input image format: ${resolved}

What it means

The service sniffs the file's magic bytes via detectMediaType and requires the detected MIME type to start with image/. Renamed files whose actual content is not an image (PDFs, videos, text) are rejected.

Source

Thrown at packages/core/src/media/service.ts:529

    const now = Date.now();
    for (const job of this.jobStore.list()) {
      if (job.artifact && Date.parse(job.artifact.expiresAt) <= now) this.artifactStore.delete(job.artifact);
    }
    for (const job of this.jobStore.deleteOlderThan(now - jobRetentionDays * 24 * 60 * 60 * 1000)) this.artifactStore.delete(job.artifact);
  }

  private validateImages(value: unknown, min: number, max: number): string[] {
    const raw = typeof value === "string" ? [value] : Array.isArray(value) ? value : [];
    if (raw.length < min || raw.length > max || raw.some((item) => typeof item !== "string" || !item.trim())) {
      throw new Error(`images must contain between ${min} and ${max} local image paths.`);
    }
    const roots = mediaInputRoots(this.requireRuntimeConfig().allowedInputRoots);
    return raw.map((item) => {
      const resolved = realpathSync(expandHome(String(item).trim()));
      if (!roots.some((root) => isPathInside(resolved, root))) throw new Error(`Input image is outside allowed roots: ${resolved}`);
      const stats = statSync(resolved);
      if (!stats.isFile() || stats.size <= 0 || stats.size > maxInputBytes) throw new Error(`Input image must be a non-empty regular file no larger than ${maxInputBytes} bytes.`);
      if (!detectMediaType(resolved).mimeType?.startsWith("image/")) throw new Error(`Unsupported input image format: ${resolved}`);
      return resolved;
    });
  }

  private finishCanceled(job: MediaJob, message: string): MediaJob {
    const next = this.jobStore.update(job.id, {
      error: { code: "canceled", message, retryable: false },
      finishedAt: new Date().toISOString(),
      status: "canceled"
    });
    this.completions.get(job.id)?.resolve(next);
    this.completions.delete(job.id);
    return next;
  }

  private publicJob(job: MediaJob): PublicMediaJob {
    const { artifact, idempotencyKeyHash: _idempotencyKeyHash, ...rest } = job;
    return {

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Convert the actual content to a supported image format (PNG/JPEG/WebP)
  2. Verify the source produces real image bytes before saving
  3. Check the file with `file` or a magic-byte sniff on the producer side

Example fix

// before
images: ["/data/report.png"] // actually a PDF
// after
images: ["/data/report-page1.png"] // converted with pdftoppm
Defensive patterns

Strategy: validation

Validate before calling

const { mimeType } = detectMediaType(p); if (!mimeType?.startsWith("image/")) throw new TypeError(`not an image: ${p}`);

Type guard

const isImageFile = (p: string): boolean => (detectMediaType(p).mimeType ?? "").startsWith("image/");

Try / catch

try { await call(); } catch (e) { if (e instanceof Error && e.message.includes("Unsupported input image format")) return convertFile(); throw e; }

Prevention

When it happens

Trigger: Passing a file whose detected MIME is not image/* — e.g. a .png extension on a PDF, a video poster pulled as .mp4, or arbitrary binary data.

Common situations: User-uploaded files trusted by extension, mislabeled downloads, or attempting to feed documents into image editing models.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/bda9bd14ddb9a0f5. Report an issue: GitHub.