Mintplex-Labs/anything-llm · warning

Refusing to download ${filename}: ${mismatch}.

Error message

Refusing to download ${filename}: ${mismatch}.

What it means

Returned (HTTP 409) by GET /api/v1/deliverables/:filename when fileExtensionMismatch(filepath, ext) detects that the file's actual content (sniffed bytes) does not match its declared extension. The service deliberately refuses to serve mislabeled files to prevent content-type confusion attacks — e.g. an .html deliverable whose bytes are really a PNG, which browsers would render instead of download.

Source

Thrown at open-computer/services/interface-service/routes/deliverables.js:32

    try {
      res.json({ deliverables: readManifest(deliverablesDir) });
    } catch {
      res.json({ deliverables: [] });
    }
  });

  app.get("/api/v1/deliverables/:filename", (req, res) => {
    const filename = safeFilename(req.params.filename);
    const filepath = path.join(deliverablesDir, filename);

    if (!fs.existsSync(filepath)) {
      return res.status(404).json({ error: `File not found: ${filename}` });
    }

    const ext = filename.split(".").pop()?.toLowerCase();
    const mismatch = fileExtensionMismatch(filepath, ext);
    if (mismatch) {
      return res.status(409).json({
        error: `Refusing to download ${filename}: ${mismatch}.`,
      });
    }

    res.setHeader("Content-Type", MIME_TYPES[ext] || "application/octet-stream");
    res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
    fs.createReadStream(filepath).pipe(res);
  });

  app.delete("/api/v1/deliverables/:filename", (req, res) => {
    const filename = safeFilename(req.params.filename);
    const filepath = path.join(deliverablesDir, filename);

    try {
      fs.unlinkSync(filepath);
    } catch {}

    try {

View on GitHub (pinned to 3aec848f28)

Solutions

  1. Rename the file on disk so its extension matches the real content (mv deliverables/report.html deliverables/report.md)
  2. Regenerate the deliverable and let the agent name it correctly this run
  3. Convert the file to the claimed format instead of just renaming, if the content is what you want to keep
  4. Do not try to bypass the guard — it exists to stop MIME confusion; fix the file instead

Example fix

# before: content is markdown but the file is named .html
# browser would render it on download → 409 Refusing to download
ls deliverables/report.html

# after: align the extension with the actual content
mv deliverables/report.html deliverables/report.md
curl -OJ "$BASE/api/v1/deliverables/report.md"
Defensive patterns

Strategy: validation

Validate before calling

// Client-side sanity check: extension should plausibly match the bytes you expect
const ext = name.split('.').pop().toLowerCase();
const sig = await file.slice(0, 4).arrayBuffer(); // when you control the file before upload/serve
if (!extensionMatchesMagicBytes(ext, sig)) renameToMatch(ext, file);

Try / catch

try {
  const res = await fetch(url);
  if (res.status === 409) {
    const {error} = await res.json(); // tells you the detected mismatch
    throw new Error(error); // fix the file's name/content — do not retry unchanged
  }
} catch (e) { throw e; }

Prevention

When it happens

Trigger: The agent saved content under the wrong extension (markdown into a .png, CSV into .xlsx); someone manually renamed a file in the deliverables directory; a truncated or corrupted file whose magic bytes no longer match its type; a genuinely malicious upload disguised with a benign extension.

Common situations: LLM agents naming generated artifacts by prompt topic rather than actual format; users renaming files to force a different download behavior; partial writes from a crashed generation run; security scans probing the file server.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/de08f293695e944d. Report an issue: GitHub.