{"record":{"id":"de08f293695e944d","repo":"Mintplex-Labs/anything-llm","slug":"refusing-to-download-filename-mismatch","errorCode":null,"errorMessage":"Refusing to download ${filename}: ${mismatch}.","messagePattern":"Refusing to download (.+?): (.+?)\\.","errorType":"http","errorClass":null,"httpStatus":409,"severity":"warning","filePath":"open-computer/services/interface-service/routes/deliverables.js","lineNumber":32,"sourceCode":"    try {\n      res.json({ deliverables: readManifest(deliverablesDir) });\n    } catch {\n      res.json({ deliverables: [] });\n    }\n  });\n\n  app.get(\"/api/v1/deliverables/:filename\", (req, res) => {\n    const filename = safeFilename(req.params.filename);\n    const filepath = path.join(deliverablesDir, filename);\n\n    if (!fs.existsSync(filepath)) {\n      return res.status(404).json({ error: `File not found: ${filename}` });\n    }\n\n    const ext = filename.split(\".\").pop()?.toLowerCase();\n    const mismatch = fileExtensionMismatch(filepath, ext);\n    if (mismatch) {\n      return res.status(409).json({\n        error: `Refusing to download ${filename}: ${mismatch}.`,\n      });\n    }\n\n    res.setHeader(\"Content-Type\", MIME_TYPES[ext] || \"application/octet-stream\");\n    res.setHeader(\"Content-Disposition\", `attachment; filename=\"${filename}\"`);\n    fs.createReadStream(filepath).pipe(res);\n  });\n\n  app.delete(\"/api/v1/deliverables/:filename\", (req, res) => {\n    const filename = safeFilename(req.params.filename);\n    const filepath = path.join(deliverablesDir, filename);\n\n    try {\n      fs.unlinkSync(filepath);\n    } catch {}\n\n    try {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/open-computer/services/interface-service/routes/deliverables.js#L14-L50","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Rename the file on disk so its extension matches the real content (mv deliverables/report.html deliverables/report.md)","Regenerate the deliverable and let the agent name it correctly this run","Convert the file to the claimed format instead of just renaming, if the content is what you want to keep","Do not try to bypass the guard — it exists to stop MIME confusion; fix the file instead"],"exampleFix":"# before: content is markdown but the file is named .html\n# browser would render it on download → 409 Refusing to download\nls deliverables/report.html\n\n# after: align the extension with the actual content\nmv deliverables/report.html deliverables/report.md\ncurl -OJ \"$BASE/api/v1/deliverables/report.md\"","handlingStrategy":"validation","validationCode":"// Client-side sanity check: extension should plausibly match the bytes you expect\nconst ext = name.split('.').pop().toLowerCase();\nconst sig = await file.slice(0, 4).arrayBuffer(); // when you control the file before upload/serve\nif (!extensionMatchesMagicBytes(ext, sig)) renameToMatch(ext, file);","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(url);\n  if (res.status === 409) {\n    const {error} = await res.json(); // tells you the detected mismatch\n    throw new Error(error); // fix the file's name/content — do not retry unchanged\n  }\n} catch (e) { throw e; }","preventionTips":["Name generated files by their actual content type, not by the prompt topic","When producing files for the deliverables dir, write the correct extension at creation time","Never retry a 409 — the guard is deterministic until the file changes"],"tags":["http-409","content-sniffing","mime-mismatch","file-download","security-guard"],"backgroundTag":"file-content-type-mismatch","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}