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
- 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).
- Confirm free disk space and that the path is on a writable filesystem (not a read-only install dir).
- 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).
- 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
- Resolve the AI config storage path under a user-writable directory at install time.
- Run a startup self-check: attempt to create+delete a temp file in the config dir.
- Run the process under a user that owns the config directory.
- Monitor free disk space on the volume hosting the config file.
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.attachment.localParseFailed
- ai.chat.history.deleteMessagesFailed
- ai.chat.history.loadSessionsFailed
- ai.chat.history.persistSessionsFailed
- ai.chat.history.loadMessagesFailed
AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14).
Data as JSON: /api/errors/ad860d6ac1a23f4d.
Report an issue: GitHub.