{"record":{"id":"026633423e115d59","repo":"OtterMind/Chat2DB","slug":"ai-chat-history-persistsessionsfailed","errorCode":"ai.chat.history.persistSessionsFailed","errorMessage":"ai.chat.history.persistSessionsFailed","messagePattern":"ai\\.chat\\.history\\.persistSessionsFailed","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":215,"sourceCode":"            return new ArrayList<>();\n        }\n        try {\n            SessionsFile file = objectMapper.readValue(path.toFile(), SessionsFile.class);\n            return file.getSessions() != null ? file.getSessions() : new ArrayList<>();\n        } catch (Exception e) {\n            throw new BusinessException(\"ai.chat.history.loadSessionsFailed\", new Object[]{path, e.getMessage()}, e);\n        }\n    }\n\n    private void persistSessions(Long userId, List<AiChatSession> sessions) {\n        Path path = sessionsPath(userId);\n        try {\n            Files.createDirectories(path.getParent());\n            SessionsFile file = new SessionsFile();\n            file.setSessions(sessions);\n            objectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);\n        } catch (IOException e) {\n            throw new BusinessException(\"ai.chat.history.persistSessionsFailed\", new Object[]{path, e.getMessage()}, e);\n        }\n    }\n\n    private List<AiChatMessage> loadMessages(String sessionId) {\n        Path path = messagesPath(sessionId);\n        if (!Files.exists(path)) {\n            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);","sourceCodeStart":197,"sourceCodeEnd":233,"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#L197-L233","documentation":"Thrown by AiChatHistoryServiceImpl.persistSessions in the catch(IOException) while writing sessions-<userId>.json (creating parent dirs or serializing). Args are {path, e.getMessage()}. Any write failure aborts the persistence of the user's session list.","triggerScenarios":"Files.createDirectories(path.getParent()) fails (permission denied, read-only mount), or the Jackson writeValue fails: disk full, quota exceeded, the path is on a read-only filesystem, or the directory was removed under the process.","commonSituations":"Disk full or inode exhaustion in the chat2db data dir; the process user lacks write permission on <basePath>/ai-chat-history/; a read-only container volume; a parent path collision with a regular file.","solutions":["Verify the process user has read+write+execute on ConfigUtils.getBasePath()/ai-chat-history and that the volume has free space/inodes.","If read-only, set chat2db.base.path (or the equivalent) to a writable location.","Free disk space if the volume is full; the operation is retried on the next session mutation.","Ensure no regular file occupies the ai-chat-history directory name."],"exampleFix":"// before: persisting to a read-only/full volume\nobjectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);\n\n// after: confirm writability and report a clear error\nFiles.createDirectories(path.getParent());\nif (!Files.isWritable(path.getParent())) {\n    throw new IllegalStateException(\"ai-chat-history dir not writable: \" + path.getParent());\n}\nobjectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);","handlingStrategy":"validation","validationCode":"Path dir = Paths.get(ConfigUtils.getBasePath(), \"ai-chat-history\");\nFiles.createDirectories(dir);\nif (!Files.isWritable(dir)) {\n    throw new IllegalStateException(\"not writable: \" + dir);\n}","typeGuard":null,"tryCatchPattern":"try {\n    history.createSession(...);\n} catch (BusinessException e) {\n    if (\"ai.chat.history.persistSessionsFailed\".equals(e.getCode())) {\n        return ResponseEntity.status(507).body(\"storage full or read-only\");\n    }\n    throw e;\n}","preventionTips":["Keep free space and inodes on the chat2db data volume.","Run the process as a user with write permission on the data dir.","Mount the container volume read-write, not read-only."],"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"}