{"record":{"id":"36643bcc17d5da1e","repo":"OtterMind/Chat2DB","slug":"ai-attachment-emptycontent","errorCode":"ai.attachment.emptyContent","errorMessage":"ai.attachment.emptyContent","messagePattern":"ai\\.attachment\\.emptyContent","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":155,"sourceCode":"    }\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;\n    }\n\n    private void validateExtension(String extension) {\n        if (StringUtils.isBlank(extension) || !SUPPORTED_EXTENSIONS.contains(extension)) {\n            throw new BusinessException(\"ai.attachment.unsupportedFileExtensions\");","sourceCodeStart":137,"sourceCodeEnd":173,"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#L137-L173","documentation":"Thrown by AiAttachmentServiceImpl.parse(String, InputStream) when the parsed content, after normalizeText(), is blank. The file was readable and the type was supported, but no extractable text remained. The i18n message resolves to 'File content is empty or cannot be parsed'.","triggerScenarios":"A supported file parses successfully but yields no text: a scanned/image-only PDF with no OCR text layer; an empty .txt/.md/.json/.csv; a docx/xlsx whose visible cells/paragraphs are all whitespace; a workbook where only header rows exceed the row/column caps leaving no body text.","commonSituations":"Users uploading scanned PDFs expecting text extraction; uploading a template docx that is visually empty; uploading a spreadsheet whose data lives beyond MAX_SHEET_ROWS(100)/MAX_SHEET_COLUMNS(20) so the bounded reader returns nothing; a csv with only a header and no rows.","solutions":["Inform the user the file yielded no extractable text and ask for a text-based version (e.g. OCR the scanned PDF first).","For spreadsheets, confirm the data is within the first 100 rows / 20 columns the parser reads.","Pre-validate non-empty content client-side for plain-text types before uploading."],"exampleFix":"// before: uploading a scanned PDF (no text layer)\nservice.parse(req); // -> emptyContent\n\n// after: run OCR to produce a text PDF, or upload the extracted text as .txt\n// client-side pre-check for plain text types\nif (isTextType(ext) && file.size === 0) {\n    notifyUser(\"file is empty\");\n    return;\n}","handlingStrategy":"validation","validationCode":"// client-side: warn on likely-empty content for text types\nif (isPlainText(ext) && file.size === 0) {\n    notifyUser(\"file is empty\"); return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    service.parse(param);\n} catch (BusinessException e) {\n    if (\"ai.attachment.emptyContent\".equals(e.getCode())) {\n        return ResponseEntity.unprocessableEntity()\n            .body(\"no extractable text; for scanned PDFs run OCR first\");\n    }\n    throw e;\n}","preventionTips":["For scanned PDFs, OCR before upload to create a text layer.","For spreadsheets, keep data within the first 100 rows / 20 columns.","Pre-validate non-empty content for plain-text types before uploading."],"tags":["ai","attachment","parsing","content-validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}