{"record":{"id":"6076c8a03a80f1c4","repo":"OtterMind/Chat2DB","slug":"ai-attachment-filepathrequired","errorCode":"ai.attachment.filePathRequired","errorMessage":"ai.attachment.filePathRequired","messagePattern":"ai\\.attachment\\.filePathRequired","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":68,"sourceCode":"        supportedExtensions.addAll(TABULAR_EXTENSIONS);\n        SUPPORTED_EXTENSIONS = supportedExtensions;\n    }\n\n    public ChatAttachment parse(AiAttachmentParseRequest param) {\n        if (param == null || param.getInputStream() == null) {\n            throw new BusinessException(\"ai.attachment.inputStreamRequired\");\n        }\n        String fileName = StringUtils.defaultIfBlank(param.getFileName(), \"attachment\");\n        try (InputStream inputStream = param.getInputStream()) {\n            return parse(fileName, inputStream);\n        } catch (IOException e) {\n            throw new BusinessException(\"ai.attachment.parseFailed\", new Object[]{fileName, e.getMessage()}, e);\n        }\n    }\n\n    public ChatAttachment parse(AiLocalAttachmentParseRequest param) {\n        if (param == null || StringUtils.isBlank(param.getFilePath())) {\n            throw new BusinessException(\"ai.attachment.filePathRequired\");\n        }\n        File file = new File(param.getFilePath().trim());\n        if (!file.exists() || !file.isFile()) {\n            throw new BusinessException(\"ai.attachment.fileNotFound\");\n        }\n        String fileName = StringUtils.defaultIfBlank(param.getFileName(), file.getName());\n        try (InputStream inputStream = new FileInputStream(file)) {\n            return parse(fileName, inputStream);\n        } catch (IOException e) {\n            throw new BusinessException(\"ai.attachment.localParseFailed\", new Object[]{file.getPath(), e.getMessage()}, e);\n        }\n    }\n\n    public String buildStructuredContext(List<ChatAttachment> attachments) {\n        if (CollectionUtils.isEmpty(attachments)) {\n            return null;\n        }\n        StringBuilder builder = new StringBuilder(4096);","sourceCodeStart":50,"sourceCodeEnd":86,"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#L50-L86","documentation":"Thrown by AiAttachmentServiceImpl.parse(AiLocalAttachmentParseRequest) when the parse request is null or its filePath is blank. This is the local-file parse entrypoint (server-side path), distinct from the InputStream-based overload; it refuses to proceed without a concrete filesystem path. The i18n message resolves to 'File path is required'.","triggerScenarios":"Calling AiAttachmentService.parse(AiLocalAttachmentParseRequest) where param is null, or param.getFilePath() returns null/empty/whitespace-only. This path is used when the JCEF/desktop or web renderer submits a server-side file path rather than an uploaded stream.","commonSituations":"Frontend sends an AiLocalAttachmentParseRequest with filePath unset because the upload step failed silently; a serialized request lost the field; a test constructs the request object without setting filePath; the user submitted the form before the path was populated.","solutions":["Ensure the caller sets param.setFilePath() to a non-blank, absolute server path before calling parse().","If the renderer is uploading bytes rather than referencing a server path, call parse(AiAttachmentParseRequest) (the InputStream overload) instead of the local-path overload.","Add a null/blank check on filePath in the controller or service caller and return a 400 with a clear field-level error before reaching the domain service."],"exampleFix":"// before\nAiLocalAttachmentParseRequest req = new AiLocalAttachmentParseRequest();\n// filePath never set\nservice.parse(req);\n\n// after\nAiLocalAttachmentParseRequest req = new AiLocalAttachmentParseRequest();\nreq.setFilePath(uploadedTempPath);\nif (StringUtils.isBlank(req.getFilePath())) {\n    return ResponseEntity.badRequest().body(\"filePath is required\");\n}\nservice.parse(req);","handlingStrategy":"validation","validationCode":"// caller-side guard before parse(AiLocalAttachmentParseRequest)\nAiLocalAttachmentParseRequest param = /* ... */;\nif (param == null || StringUtils.isBlank(param.getFilePath())) {\n    return ResponseEntity.badRequest().body(\"filePath is required\");\n}\nFile f = new File(param.getFilePath().trim());\nif (!f.isFile()) {\n    return ResponseEntity.badRequest().body(\"file not found\");\n}\nservice.parse(param);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set filePath on AiLocalAttachmentParseRequest from a validated server-side temp path.","Prefer the InputStream overload (parse(AiAttachmentParseRequest)) when you already have the bytes.","Validate required request fields in the controller layer, not only in the domain service."],"tags":["ai","attachment","input-validation","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}