{"record":{"id":"8650cbe017e014d8","repo":"OtterMind/Chat2DB","slug":"ai-chat-history-persistmessagesfailed","errorCode":"ai.chat.history.persistMessagesFailed","errorMessage":"ai.chat.history.persistMessagesFailed","messagePattern":"ai\\.chat\\.history\\.persistMessagesFailed","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/AiChatHistoryServiceImpl.java","lineNumber":240,"sourceCode":"            return new ArrayList<>();\n        }\n        try {\n            MessagesFile file = objectMapper.readValue(path.toFile(), MessagesFile.class);\n            return file.getMessages() != null ? file.getMessages() : new ArrayList<>();\n        } catch (Exception e) {\n            throw new BusinessException(\"ai.chat.history.loadMessagesFailed\", new Object[]{path, e.getMessage()}, e);\n        }\n    }\n\n    private void persistMessages(String sessionId, List<AiChatMessage> messages) {\n        Path path = messagesPath(sessionId);\n        try {\n            Files.createDirectories(path.getParent());\n            MessagesFile file = new MessagesFile();\n            file.setMessages(messages);\n            objectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);\n        } catch (IOException e) {\n            throw new BusinessException(\"ai.chat.history.persistMessagesFailed\", new Object[]{path, e.getMessage()}, e);\n        }\n    }\n\n    private void touchSession(Long userId, String sessionId) {\n        List<AiChatSession> sessions = loadSessions(userId);\n        sessions.stream()\n                .filter(s -> Objects.equals(s.getId(), sessionId))\n                .findFirst()\n                .ifPresent(s -> s.setGmtModified(LocalDateTime.now()));\n        persistSessions(userId, sessions);\n    }\n\n    @Data\n    public static class SessionsFile {\n        private List<AiChatSession> sessions = new ArrayList<>();\n    }\n\n    @Data","sourceCodeStart":222,"sourceCodeEnd":258,"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/AiChatHistoryServiceImpl.java#L222-L258","documentation":"Thrown by AiChatHistoryServiceImpl.persistMessages in the catch(IOException) while writing <sessionId>.json (parent dir creation or serialization). Args are {path, e.getMessage()}. Any write failure aborts persistence of that session's messages, so an in-flight conversation may not be saved.","triggerScenarios":"Files.createDirectories(parent) fails or Jackson writeValue throws IOException: disk full, quota exceeded, read-only mount, permission denied, or the parent dir was removed during the call.","commonSituations":"Disk full mid-conversation; the process user lost write permission on ai-chat-history; read-only container volume; inode exhaustion.","solutions":["Confirm read+write+execute permission on <basePath>/ai-chat-history for the process user and free space/inodes.","Point chat2db.base.path at a writable volume.","Use an atomic write (temp + ATOMIC_MOVE) so partial writes never replace a good file on a transient failure.","Surface the failure to the user so they know the last message was not persisted."],"exampleFix":"// before: direct write, partial on failure\nobjectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);\n\n// after: atomic, and notify on failure\ntry {\n    Path tmp = path.resolveSibling(path.getFileName() + \".tmp\");\n    objectMapper.writerWithDefaultPrettyPrinter().writeValue(tmp.toFile(), file);\n    Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);\n} catch (IOException e) {\n    notifyUser(\"message could not be saved\");\n    throw e;\n}","handlingStrategy":"validation","validationCode":"Path dir = Paths.get(ConfigUtils.getBasePath(), \"ai-chat-history\");\nif (!Files.isWritable(dir)) {\n    throw new IllegalStateException(\"ai-chat-history not writable\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    history.addMessage(...);\n} catch (BusinessException e) {\n    if (\"ai.chat.history.persistMessagesFailed\".equals(e.getCode())) {\n        notifyUser(\"your last message could not be saved; retry\");\n        return ResponseEntity.status(507).build();\n    }\n    throw e;\n}","preventionTips":["Monitor free space on the data volume.","Use atomic writes so a transient failure never clobbers a good file.","Tell the user when a message was not persisted so they can resend."],"tags":["ai","chat-history","io","filesystem","persistence"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}