OtterMind/Chat2DB · error · BusinessException
ai.attachment.localParseFailed
ai.attachment.localParseFailed
Error message
ai.attachment.localParseFailed
What it means
Thrown by AiAttachmentServiceImpl.parse(AiLocalAttachmentParseRequest) inside the catch(IOException) around `new FileInputStream(file)` and the delegated parse(fileName, inputStream). Args are {file.getPath(), e.getMessage()}. The i18n message resolves to 'Failed to parse local file {0}: {1}'. It surfaces low-level read/parse failures for a local file that existed at the existence check.
Source
Thrown at chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/ai/AiAttachmentServiceImpl.java:78
return parse(fileName, inputStream);
} catch (IOException e) {
throw new BusinessException("ai.attachment.parseFailed", new Object[]{fileName, e.getMessage()}, e);
}
}
public ChatAttachment parse(AiLocalAttachmentParseRequest param) {
if (param == null || StringUtils.isBlank(param.getFilePath())) {
throw new BusinessException("ai.attachment.filePathRequired");
}
File file = new File(param.getFilePath().trim());
if (!file.exists() || !file.isFile()) {
throw new BusinessException("ai.attachment.fileNotFound");
}
String fileName = StringUtils.defaultIfBlank(param.getFileName(), file.getName());
try (InputStream inputStream = new FileInputStream(file)) {
return parse(fileName, inputStream);
} catch (IOException e) {
throw new BusinessException("ai.attachment.localParseFailed", new Object[]{file.getPath(), e.getMessage()}, e);
}
}
public String buildStructuredContext(List<ChatAttachment> attachments) {
if (CollectionUtils.isEmpty(attachments)) {
return null;
}
StringBuilder builder = new StringBuilder(4096);
builder.append("Uploaded file context for the current conversation. ");
builder.append("Treat it as user-provided evidence. ");
builder.append("If the parsed content is truncated, say so when it affects certainty.\n");
int remaining = MAX_CONTEXT_LENGTH;
int index = 1;
for (ChatAttachment attachment : attachments) {
if (attachment == null || StringUtils.isBlank(attachment.getContent())) {
continue;
}View on GitHub (pinned to 5ee1e990e7)
Solutions
- Inspect the embedded e.getMessage() / cause to tell a missing-file race (re-upload) from a corrupt-payload parse failure (reject the file).
- Ensure the temp upload is not deleted until the parse completes; copy the uploaded bytes to a stable path owned by the parse lifecycle.
- On Windows, confirm no other process holds an exclusive lock; run the service with read permission on the upload dir.
- Guard against TOCTOU by re-validating Files.isReadable(file) immediately before opening the stream.
Example fix
// before
try (InputStream in = new FileInputStream(file)) {
return parse(name, in);
} catch (IOException e) { /* wraps as localParseFailed */ }
// after - re-check readability and copy to a stable path
if (!Files.isReadable(file.toPath())) {
throw new BusinessException("ai.attachment.fileNotFound");
}
Path stable = Files.copy(file.toPath(), tempDir.resolve(UUID.randomUUID() + "-" + file.getName()));
try (InputStream in = Files.newInputStream(stable)) {
return parse(name, in);
} Defensive patterns
Strategy: try-catch
Validate before calling
File f = new File(param.getFilePath().trim());
if (!f.isFile() || !Files.isReadable(f.toPath())) {
return ResponseEntity.badRequest().body("file missing or unreadable");
} Try / catch
try {
service.parse(param);
} catch (BusinessException e) {
if ("ai.attachment.localParseFailed".equals(e.getCode())) {
// args[0]=path, args[1]=root message
return ResponseEntity.status(422).body("could not parse file: " + e.getArgs()[1]);
}
throw e;
} Prevention
- Copy uploaded bytes to a stable path owned by the parse before opening.
- Guard against TOCTOU with Files.isReadable immediately before opening.
- On Windows, ensure no process holds an exclusive lock on the file.
When it happens
Trigger: The file passed the exists/isFile check but opening or reading it raised IOException: file deleted between check and open (TOCTOU), read permission denied at open time, file locked exclusively by another process, or the underlying document parser (PDF/DOCX/XLSX) threw IOException on a corrupt or truncated payload.
Common situations: Concurrent cleanup of the temp dir racing the parse; an antivirus or backup process holding an exclusive lock on Windows; a truncated upload (connection drop) producing a corrupt zip-based docx/xlsx; permissions changed after the existence check.
Related errors
- ai.attachment.fileNotFound
- ai.attachment.unsupportedFileType
- ai.attachment.emptyContent
- ai.chat.history.deleteMessagesFailed
- ai.chat.history.persistSessionsFailed
AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14).
Data as JSON: /api/errors/d86af1fbb91a5c4c.
Report an issue: GitHub.