OtterMind/Chat2DB · critical · IllegalStateException

Failed to persist ai config to

Error message

Failed to persist ai config to 

What it means

Thrown by persistToDisk() when Jackson/object-mapper file write of the AI model config (StorageData, with encrypted API keys) to storagePath raises an IOException. It is wrapped in an IllegalStateException so the original I/O cause is preserved as the cause. The method is synchronized, so this is the terminal failure after every successful in-memory config mutation (save/delete).

Source

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

            userConfigMap.clear();
            userConfigMap.putAll(loadedConfigMap);
        } catch (Exception e) {
            throw new IllegalStateException("Failed to load ai config from " + storagePath, e);
        }
    }

    private synchronized void persistToDisk() {
        try {
            Files.createDirectories(storagePath.getParent());
            StorageData data = new StorageData();
            List<AiModelConfig> all = userConfigMap.values().stream()
                    .flatMap(List::stream)
                    .map(this::encryptedCopy)
                    .collect(Collectors.toList());
            data.setConfigs(all);
            objectMapper.writerWithDefaultPrettyPrinter().writeValue(storagePath.toFile(), data);
        } catch (IOException e) {
            throw new IllegalStateException("Failed to persist ai config to " + storagePath, e);
        }
    }

    private AiModelConfig encryptedCopy(AiModelConfig config) {
        AiModelConfig copy = new AiModelConfig();
        BeanUtils.copyProperties(config, copy);
        copy.setApiKey(encryptApiKey(config.getApiKey()));
        return copy;
    }

    private String encryptApiKey(String apiKey) {
        if (aesGcmUtil == null || apiKey == null || apiKey.isEmpty()) {
            return apiKey;
        }
        return aesGcmUtil.encryptAiModelApiKey(apiKey);
    }

    private String decryptApiKey(String storedApiKey) {

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Check the exception message for the exact storagePath and verify the process has write permission on its parent (ls -ld on the dir, chmod/chown as needed).
  2. Confirm free disk space and that the path is on a writable filesystem (not a read-only install dir).
  3. If running the desktop package from a system-protected location, move/configure the storage path to a user-writable directory (e.g. user home config dir).
  4. Ensure no second Chat2DB instance or external process locks the file; restart the process after fixing permissions.

Example fix

// before: storagePath resolved under a read-only install dir
Path storagePath = appDir.resolve("ai-config.json");

// after: resolve under a user-writable config directory
Path storagePath = Paths.get(System.getProperty("user.home"), ".chat2db", "ai-config.json");
Defensive patterns

Strategy: try-catch

Validate before calling

Path parent = storagePath.getParent();
if (!Files.isWritable(parent)) {
    throw new IllegalStateException("Config dir not writable: " + parent);
}
if (parent.toFile().getUsableSpace() < 1024L) {
    throw new IllegalStateException("Insufficient disk space for config write");
}

Try / catch

try {
    aiModelConfigService.save(config);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Failed to persist ai config to ")) {
        log.error("AI config persistence failed for path; check permissions/disk", e);
        // surface to user without losing in-memory state
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Any call that mutates AI model config (create/update/delete config) which then calls persistToDisk(); the write fails because the parent directory is not writable, the disk is full, the path is read-only, or another process holds an exclusive lock on the file.

Common situations: Desktop/JCEF package installed to Program Files without write privileges; storagePath resolving to a read-only install dir; full disk; container running as a UID that cannot write the configured config dir; path on a network mount that dropped.

Related errors


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