{"record":{"id":"0f12d6d1875b19bc","repo":"OtterMind/Chat2DB","slug":"ai-attachment-unsupportedfiletype","errorCode":"ai.attachment.unsupportedFileType","errorMessage":"ai.attachment.unsupportedFileType","messagePattern":"ai\\.attachment\\.unsupportedFileType","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":150,"sourceCode":"    }\n\n    public boolean hasAttachment(List<ChatAttachment> attachments) {\n        return attachments != null && attachments.stream()\n                .anyMatch(item -> item != null && StringUtils.isNotBlank(item.getContent()));\n    }\n\n    private ChatAttachment parse(String fileName, InputStream inputStream) throws IOException {\n        String extension = StringUtils.lowerCase(FilenameUtils.getExtension(fileName));\n        validateExtension(extension);\n\n        String content = switch (extension) {\n            case \"pdf\" -> parsePdf(inputStream);\n            case \"docx\" -> parseDocx(inputStream);\n            case \"doc\" -> parseDoc(inputStream);\n            case \"csv\" -> parseCsv(inputStream);\n            case \"xls\", \"xlsx\" -> parseWorkbook(inputStream);\n            case \"md\", \"txt\", \"json\" -> readPlainText(inputStream);\n            default -> throw new BusinessException(\"ai.attachment.unsupportedFileType\");\n        };\n\n        String normalizedContent = normalizeText(content);\n        if (StringUtils.isBlank(normalizedContent)) {\n            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;","sourceCodeStart":132,"sourceCodeEnd":168,"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#L132-L168","documentation":"Thrown by the `default` arm of the extension switch in AiAttachmentServiceImpl.parse(String, InputStream). It is a defensive safety-net: validateExtension() runs immediately before the switch and already rejects any extension outside the supported set, so under normal control flow this branch is unreachable. The i18n message resolves to 'This file type is not supported'.","triggerScenarios":"Only reachable if validateExtension is bypassed or if SUPPORTED_EXTENSIONS and the switch cases drift out of sync (e.g. a new extension added to SUPPORTED_EXTENSIONS without a matching parser case). The switch handles pdf, docx, doc, csv, xls, xlsx, md, txt, json - exactly the supported set.","commonSituations":"A maintainer adds an extension to DOCUMENT_EXTENSIONS/TABULAR_EXTENSIONS (which feeds SUPPORTED_EXTENSIONS) so it passes validateExtension, but forgets to add a `case` to the switch, falling through to default. Regression after refactoring the parse dispatch.","solutions":["If you hit this in production, check whether SUPPORTED_EXTENSIONS was extended without a matching switch case, and add the parser.","If you are a maintainer, keep SUPPORTED_EXTENSIONS and the switch in sync, or derive the switch source-of-truth from a single map of extension->parser.","Callers cannot legitimately hit this; treat it as an internal bug and report the extension value in the exception args."],"exampleFix":"// before: extension added to supported set but no case\nprivate static final Set<String> DOCUMENT_EXTENSIONS =\n    Set.of(\"pdf\",\"doc\",\"docx\",\"md\",\"txt\",\"json\",\"html\"); // html passes validateExtension\n// switch has no case \"html\" -> falls to default\n\n// after: add the parser case, or reject in validateExtension\nprivate static final Set<String> DOCUMENT_EXTENSIONS =\n    Set.of(\"pdf\",\"doc\",\"docx\",\"md\",\"txt\",\"json\");\n// and in the switch:\ncase \"html\" -> parseHtml(inputStream);","handlingStrategy":"validation","validationCode":"// This branch is normally unreachable; if reached it is an internal bug.\n// Validate extension against the same set the switch covers before delegating.\nString ext = StringUtils.lowerCase(FilenameUtils.getExtension(fileName));\nif (!Set.of(\"pdf\",\"doc\",\"docx\",\"csv\",\"xls\",\"xlsx\",\"md\",\"txt\",\"json\").contains(ext)) {\n    return ResponseEntity.badRequest().body(\"unsupported type: \" + ext);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep SUPPORTED_EXTENSIONS and the parse switch in sync when adding types.","Treat this error as a defect report; capture the extension value for debugging.","Add a unit test asserting every SUPPORTED_EXTENSIONS entry has a switch case."],"tags":["ai","attachment","parsing","defensive-code","internal-bug"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}