{"record":{"id":"5d3569f4fbe7e1d8","repo":"OtterMind/Chat2DB","slug":"ai-model-config-required","errorCode":"ai.model.config.required","errorMessage":"ai.model.config.required","messagePattern":"ai\\.model\\.config\\.required","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/AiModelConfigServiceImpl.java","lineNumber":238,"sourceCode":"        if (provider == AiProviderEnum.MINIMAX) {\n            String baseUrl = trimToNull(request.getBaseUrl());\n            if (StringUtils.isNotBlank(baseUrl) && StringUtils.containsIgnoreCase(baseUrl, \"/anthropic\")) {\n                return testAnthropicCompatibleConfig(request);\n            }\n            return testOpenAiCompatibleConfig(request, DEFAULT_MINIMAX_BASE_URL);\n        }\n        return ModelConfigTestResponse.failure(null, null,\n                \"Connection test currently supports OpenAI-compatible models only.\");\n    }\n\n    public synchronized AiRuntimeModel resolveRuntimeModel(AiChatRuntimeResolveRequest request) {\n        Long userId = identityService.currentUserId();\n        AiModelConfig baseConfig = null;\n        boolean systemPreset = false;\n        if (StringUtils.isNotBlank(request.getModelConfigId())) {\n            String modelConfigId = request.getModelConfigId().trim();\n            if (modelConfigId.startsWith(PRESET_VALUE_PREFIX)) {\n                throw new BusinessException(ERROR_MODEL_CONFIG_REQUIRED);\n            } else {\n                String resolvedId = modelConfigId.startsWith(CONFIG_VALUE_PREFIX)\n                        ? modelConfigId.substring(CONFIG_VALUE_PREFIX.length())\n                        : modelConfigId;\n                baseConfig = findById(userConfigMap.getOrDefault(userId, new ArrayList<>()), resolvedId);\n                if (Objects.isNull(baseConfig)) {\n                    throw new BusinessException(\"ai.model.config.notFound\");\n                }\n            }\n        } else if (StringUtils.isNotBlank(request.getProvider()) && StringUtils.isNotBlank(request.getModel())) {\n            baseConfig = new AiModelConfig();\n            baseConfig.setProvider(request.getProvider());\n            baseConfig.setModel(request.getModel());\n            baseConfig.setApiKey(request.getApiKey());\n            baseConfig.setBaseUrl(request.getBaseUrl());\n            baseConfig.setProjectId(request.getProjectId());\n            baseConfig.setLocation(request.getLocation());\n            baseConfig.setTemperature(request.getTemperature());","sourceCodeStart":220,"sourceCodeEnd":256,"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/AiModelConfigServiceImpl.java#L220-L256","documentation":"Thrown by AiModelConfigServiceImpl.resolveRuntimeModel when request.getModelConfigId() (trimmed) starts with the PRESET_VALUE_PREFIX 'preset:'. In Community mode a preset-only id is not a runnable configuration - there is no Gateway/cloud preset resolution - so the service refuses and requires a saved local config. The i18n message resolves to 'Community AI requires a local model configuration. Please save a model configuration first.'","triggerScenarios":"The frontend sends modelConfigId = 'preset:<something>' to resolveRuntimeModel. This happens when the UI lets a user pick a system-preset model directly without saving a local config, which Community does not support.","commonSituations":"A Community build reusing UI/flows that submit preset ids; a user selected a built-in preset model from a dropdown; the request was constructed from stale cached state holding a preset value.","solutions":["Save a real model configuration (provider, model, apiKey/baseUrl) via the config endpoint first and send its 'config:<id>' (or bare id) instead of a 'preset:' value.","If the user only has provider+model, send those fields (no modelConfigId) so resolveRuntimeModel builds an ad-hoc config - but note preset provider+model without credentials also fails (see error 54).","Frontend: hide/disable preset-only options in Community edition and never submit 'preset:' ids."],"exampleFix":"// before\nresolve.setModelConfigId(\"preset:openai-gpt-4\");\n\n// after\n// 1. save a local config\nAiModelConfig saved = configService.save(myConfig); // returns id=42\n// 2. reference it\nresolve.setModelConfigId(\"config:42\");","handlingStrategy":"validation","validationCode":"String id = request.getModelConfigId();\nif (id != null && id.trim().startsWith(\"preset:\")) {\n    return ResponseEntity.badRequest()\n        .body(\"Community requires a saved local model config, not a preset\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    service.resolveRuntimeModel(request);\n} catch (BusinessException e) {\n    if (\"ai.model.config.required\".equals(e.getCode())) {\n        return ResponseEntity.status(428) // precondition required\n            .body(\"save a model configuration first\");\n    }\n    throw e;\n}","preventionTips":["In Community UI, never submit 'preset:' ids; hide preset-only options.","Save a local config and send 'config:<id>'.","For ad-hoc use, send provider+model+apiKey instead of a modelConfigId."],"tags":["ai","model-config","community","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}