{"record":{"id":"41075ff0b8b36b26","repo":"OtterMind/Chat2DB","slug":"ai-attachment-unsupportedfileextensions","errorCode":"ai.attachment.unsupportedFileExtensions","errorMessage":"ai.attachment.unsupportedFileExtensions","messagePattern":"ai\\.attachment\\.unsupportedFileExtensions","errorType":"exception","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/ai/AiAttachmentServiceImpl.java","lineNumber":173,"sourceCode":"            throw new BusinessException(\"ai.attachment.emptyContent\");\n        }\n\n        boolean truncated = normalizedContent.length() > MAX_CONTENT_LENGTH;\n        String finalContent = truncated ? normalizedContent.substring(0, MAX_CONTENT_LENGTH) : normalizedContent;\n\n        ChatAttachment attachment = new ChatAttachment();\n        attachment.setFileName(fileName);\n        attachment.setFileType(extension);\n        attachment.setContentCategory(resolveContentCategory(extension));\n        attachment.setContent(finalContent);\n        attachment.setContentLength(normalizedContent.length());\n        attachment.setTruncated(truncated);\n        return attachment;\n    }\n\n    private void validateExtension(String extension) {\n        if (StringUtils.isBlank(extension) || !SUPPORTED_EXTENSIONS.contains(extension)) {\n            throw new BusinessException(\"ai.attachment.unsupportedFileExtensions\");\n        }\n    }\n\n    private String resolveContentCategory(String extension) {\n        return TABULAR_EXTENSIONS.contains(extension) ? \"TABULAR\" : \"DOCUMENT\";\n    }\n\n    private String parsePdf(InputStream inputStream) throws IOException {\n        try (PDDocument document = PDDocument.load(inputStream)) {\n            return new PDFTextStripper().getText(document);\n        }\n    }\n\n    private String parseDocx(InputStream inputStream) throws IOException {\n        try (XWPFDocument document = new XWPFDocument(inputStream);\n             XWPFWordExtractor extractor = new XWPFWordExtractor(document)) {\n            return extractor.getText();\n        }","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/ai/AiAttachmentServiceImpl.java#L155-L191","documentation":"Thrown by AiAttachmentServiceImpl.validateExtension when the extension is blank or not in SUPPORTED_EXTENSIONS. This is the primary extension gate, invoked at the top of parse(String, InputStream) before the type dispatch. The i18n message resolves to 'Only pdf, doc, docx, md, txt, json, csv, xls, and xlsx files are supported'. Supported = DOCUMENT(pdf,doc,docx,md,txt,json) + TABULAR(csv,xls,xlsx).","triggerScenarios":"Uploading a file whose extension (case-insensitive) is not in the supported set, e.g. .rtf, .html, .pptx, .png, .eml, or a file with no extension at all. FilenameUtils.getExtension returns '' for dotfiles/no-extension names, which is blank and fails the check.","commonSituations":"User drags an unsupported format into the AI attachment panel; a file renamed to .pdf but actually another format still passes this gate (it fails later at parsing); a dotfile like '.gitignore' yields a blank extension; case variants like .PDF are fine because the extension is lowercased before the check.","solutions":["Restrict the upload accept list in the renderer to the supported extensions and validate before submit.","Convert the file to a supported type (e.g. export .pptx to .pdf, save .rtf as .docx).","If you need a new type, add it to DOCUMENT_EXTENSIONS/TABULAR_EXTENSIONS AND add the matching switch case + parser."],"exampleFix":"// before: uploader accepts all files\n<input type=\"file\" />\n\n// after: limit to supported types\n<input type=\"file\"\n  accept=\".pdf,.doc,.docx,.md,.txt,.json,.csv,.xls,.xlsx\" />","handlingStrategy":"validation","validationCode":"Set<String> OK = Set.of(\"pdf\",\"doc\",\"docx\",\"md\",\"txt\",\"json\",\"csv\",\"xls\",\"xlsx\");\nString ext = StringUtils.lowerCase(FilenameUtils.getExtension(fileName));\nif (StringUtils.isBlank(ext) || !OK.contains(ext)) {\n    return ResponseEntity.badRequest().body(\"unsupported extension: \" + ext);\n}","typeGuard":null,"tryCatchPattern":"try {\n    service.parse(param);\n} catch (BusinessException e) {\n    if (\"ai.attachment.unsupportedFileExtensions\".equals(e.getCode())) {\n        return ResponseEntity.badRequest().body(e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Set the file input accept attribute to the supported extensions.","Validate extension server-side before calling parse.","Note extension is lowercased, so case does not matter; format does."],"tags":["ai","attachment","input-validation","file-type"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}