OtterMind/Chat2DB · error · BusinessException

ai.chat.history.loadSessionsFailed

ai.chat.history.loadSessionsFailed

Error message

ai.chat.history.loadSessionsFailed

What it means

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.

Source

Thrown at chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/ai/AiChatHistoryServiceImpl.java:203

    private Path sessionsPath(Long userId) {
        return baseDir.resolve("sessions-" + userId + ".json");
    }

    private Path messagesPath(String sessionId) {
        return baseDir.resolve(sessionId + ".json");
    }

    private List<AiChatSession> loadSessions(Long userId) {
        Path path = sessionsPath(userId);
        if (!Files.exists(path)) {
            return new ArrayList<>();
        }
        try {
            SessionsFile file = objectMapper.readValue(path.toFile(), SessionsFile.class);
            return file.getSessions() != null ? file.getSessions() : new ArrayList<>();
        } catch (Exception e) {
            throw new BusinessException("ai.chat.history.loadSessionsFailed", new Object[]{path, e.getMessage()}, e);
        }
    }

    private void persistSessions(Long userId, List<AiChatSession> sessions) {
        Path path = sessionsPath(userId);
        try {
            Files.createDirectories(path.getParent());
            SessionsFile file = new SessionsFile();
            file.setSessions(sessions);
            objectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);
        } catch (IOException e) {
            throw new BusinessException("ai.chat.history.persistSessionsFailed", new Object[]{path, e.getMessage()}, e);
        }
    }

    private List<AiChatMessage> loadMessages(String sessionId) {
        Path path = messagesPath(sessionId);
        if (!Files.exists(path)) {

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Back up then rename/delete the corrupt sessions-<userId>.json so loadSessions returns an empty list and the user starts fresh.
  2. Make persistSessions atomic (write to a temp file then Files.move with ATOMIC_MOVE) so a crash cannot leave a half-written file.
  3. If after an upgrade, add @JsonIgnoreProperties(ignoreUnknown=true) to AiChatSession/AiChatMessage to tolerate forward-compatible schema changes.

Example fix

// before: non-atomic write can corrupt on crash
objectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), file);

// after: atomic temp+move
Path tmp = path.resolveSibling(path.getFileName() + ".tmp");
objectMapper.writerWithDefaultPrettyPrinter().writeValue(tmp.toFile(), file);
Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
Defensive patterns

Strategy: try-catch

Try / catch

try {
    history.listSessions(userId);
} catch (BusinessException e) {
    if ("ai.chat.history.loadSessionsFailed".equals(e.getCode())) {
        Path p = (Path) e.getArgs()[0];
        backupAndQuarantine(p); // move to .corrupt, start fresh
        return history.listSessions(userId);
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/9f9378bfef46759a. Report an issue: GitHub.