{"record":{"id":"c0b0c64e9ef4cd9c","repo":"musistudio/claude-code-router","slug":"images-must-contain-between-min-and-max-loca","errorCode":null,"errorMessage":"images must contain between ${min} and ${max} local image paths.","messagePattern":"images must contain between (.+?) and (.+?) local image paths\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/media/service.ts","lineNumber":521,"sourceCode":"  private startCleanup(): void {\n    if (this.cleanupTimer) clearInterval(this.cleanupTimer);\n    this.cleanup();\n    this.cleanupTimer = setInterval(() => this.cleanup(), 60 * 60 * 1000);\n    this.cleanupTimer.unref?.();\n  }\n\n  private cleanup(): void {\n    const now = Date.now();\n    for (const job of this.jobStore.list()) {\n      if (job.artifact && Date.parse(job.artifact.expiresAt) <= now) this.artifactStore.delete(job.artifact);\n    }\n    for (const job of this.jobStore.deleteOlderThan(now - jobRetentionDays * 24 * 60 * 60 * 1000)) this.artifactStore.delete(job.artifact);\n  }\n\n  private validateImages(value: unknown, min: number, max: number): string[] {\n    const raw = typeof value === \"string\" ? [value] : Array.isArray(value) ? value : [];\n    if (raw.length < min || raw.length > max || raw.some((item) => typeof item !== \"string\" || !item.trim())) {\n      throw new Error(`images must contain between ${min} and ${max} local image paths.`);\n    }\n    const roots = mediaInputRoots(this.requireRuntimeConfig().allowedInputRoots);\n    return raw.map((item) => {\n      const resolved = realpathSync(expandHome(String(item).trim()));\n      if (!roots.some((root) => isPathInside(resolved, root))) throw new Error(`Input image is outside allowed roots: ${resolved}`);\n      const stats = statSync(resolved);\n      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.`);\n      if (!detectMediaType(resolved).mimeType?.startsWith(\"image/\")) throw new Error(`Unsupported input image format: ${resolved}`);\n      return resolved;\n    });\n  }\n\n  private finishCanceled(job: MediaJob, message: string): MediaJob {\n    const next = this.jobStore.update(job.id, {\n      error: { code: \"canceled\", message, retryable: false },\n      finishedAt: new Date().toISOString(),\n      status: \"canceled\"\n    });","sourceCodeStart":503,"sourceCodeEnd":539,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/media/service.ts#L503-L539","documentation":"validateImages enforces that the images argument is a string or array of non-empty strings whose count lies within [min, max] for the given media operation. Wrong types, empty/blank entries, or out-of-range counts throw this error before any filesystem access.","triggerScenarios":"Passing images: null/number/object, an empty or blank string entry, fewer than min or more than max paths for the operation (e.g. an image-edit operation requiring at least one input image).","commonSituations":"Omitting required reference images for edit/variation endpoints, sending too many images for a single call, or forwarding unvalidated JSON tool arguments straight into the media tool.","solutions":["Check the operation's required image count and send within min..max","Ensure every entry is a non-empty trimmed string path","Validate the images field shape (string | string[]) before calling the tool"],"exampleFix":"// before\n{ prompt: \"...\", images: [] }\n// after\n{ prompt: \"...\", images: [\"/data/allowed/input.png\"] }","handlingStrategy":"validation","validationCode":"const imgs = typeof images === \"string\" ? [images] : images;\nif (!Array.isArray(imgs) || imgs.length < min || imgs.length > max || imgs.some(i => typeof i !== \"string\" || !i.trim())) throw new TypeError(\"bad images\");","typeGuard":"const isValidImages = (v: unknown, min: number, max: number): v is string[] => (typeof v === \"string\" ? [v] : Array.isArray(v) ? v : []).length >= min && (typeof v === \"string\" ? [v] : Array.isArray(v) ? v : []).length <= max;","tryCatchPattern":"try { service.editImage(args); } catch (e) { if (e instanceof Error && e.message.startsWith(\"images must contain\")) return badRequest(); throw e; }","preventionTips":["Validate tool args against the MCP input schema before dispatch","Always send trimmed non-empty string paths"],"tags":["media","validation","input-validation"],"backgroundTag":"argument-validation-failed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}