{"record":{"id":"9f9378bfef46759a","repo":"OtterMind/Chat2DB","slug":"ai-chat-history-loadsessionsfailed","errorCode":"ai.chat.history.loadSessionsFailed","errorMessage":"ai.chat.history.loadSessionsFailed","messagePattern":"ai\\.chat\\.history\\.loadSessionsFailed","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":203,"sourceCode":"\n    private Path sessionsPath(Long userId) {\n        return baseDir.resolve(\"sessions-\" + userId + \".json\");\n    }\n\n    private Path messagesPath(String sessionId) {\n        return baseDir.resolve(sessionId + \".json\");\n    }\n\n    private List<AiChatSession> loadSessions(Long userId) {\n        Path path = sessionsPath(userId);\n        if (!Files.exists(path)) {\n            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)) {","sourceCodeStart":185,"sourceCodeEnd":221,"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#L185-L221","documentation":"Thrown by AiChatHistoryServiceImpl.loadSessions in the catch(Exception) around objectMapper.readValue of sessions-<userId>.json. Args are {path, e.getMessage()}. It catches any Exception (JSON parse error, IO error, schema mismatch), so a corrupted sessions file aborts session loading entirely.","triggerScenarios":"The file <basePath>/ai-chat-history/sessions-<userId>.json exists but cannot be deserialized: truncated/corrupt JSON (process killed mid-write), a manual edit breaking the schema, a Jackson version change that cannot bind the stored shape, or an IOException reading the file.","commonSituations":"The JVM was killed while persistSessions was writing pretty-printed JSON, leaving a half-written file; the file was hand-edited; an upgrade changed AiChatSession fields and the stored JSON no longer maps; disk corruption.","solutions":["Back up then rename/delete the corrupt sessions-<userId>.json so loadSessions returns an empty list and the user starts fresh.","Make persistSessions atomic (write to a temp file then Files.move with ATOMIC_MOVE) so a crash cannot leave a half-written file.","If after an upgrade, add @JsonIgnoreProperties(ignoreUnknown=true) to AiChatSession/AiChatMessage to tolerate forward-compatible schema changes."],"exampleFix":"// before: non-atomic write can corrupt on crash\nobjectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);\n\n// after: atomic temp+move\nPath tmp = path.resolveSibling(path.getFileName() + \".tmp\");\nobjectMapper.writerWithDefaultPrettyPrinter().writeValue(tmp.toFile(), file);\nFiles.move(tmp, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    history.listSessions(userId);\n} catch (BusinessException e) {\n    if (\"ai.chat.history.loadSessionsFailed\".equals(e.getCode())) {\n        Path p = (Path) e.getArgs()[0];\n        backupAndQuarantine(p); // move to .corrupt, start fresh\n        return history.listSessions(userId);\n    }\n    throw e;\n}","preventionTips":["Make persistSessions atomic (temp + ATOMIC_MOVE) to avoid half-written files.","Add @JsonIgnoreProperties(ignoreUnknown=true) to AiChatSession for upgrade safety.","Back up corrupt session files before discarding them."],"tags":["ai","chat-history","serialization","io","data-corruption"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}