{"record":{"id":"f990f0159eb3b612","repo":"iflytek/astron-agent","slug":"model-not-compatible-openai","errorCode":"MODEL_NOT_COMPATIBLE_OPENAI","errorMessage":"BusinessException(ResponseEnum.MODEL_NOT_COMPATIBLE_OPENAI)","messagePattern":"BusinessException\\(ResponseEnum\\.MODEL_NOT_COMPATIBLE_OPENAI\\)","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java","lineNumber":166,"sourceCode":"\n        // 2) Construct/validate URL + request body/headers\n        final String provider = normalizeProvider(request.getProvider(), true);\n        final String url = buildModelApiUrlNew(request.getEndpoint(), provider, request.getDomain());\n        final Map<String, Object> requestBody =\n                buildValidationPayload(request.getDomain(), provider);\n        final HttpHeaders headers = buildAuthHeaders(decryptedApiKey, provider);\n\n        try {\n            String responseBody = doPostModelApi(url, requestBody, headers);\n            if (isValidModelResponse(responseBody, provider)) {\n                log.info(\"Model validation passed, domain={}, endpoint={}\", request.getDomain(), url);\n                request.setApiKey(decryptedApiKey);\n                request.setEndpoint(url);\n                request.setProvider(provider);\n                saveOrUpdateModel(request);\n                return \"Model validation passed\";\n            }\n            throw new BusinessException(ResponseEnum.MODEL_NOT_COMPATIBLE_OPENAI);\n        } catch (BusinessException e) {\n            log.error(\"Model validation failed, url={}, err={}\", url, e.getMessage(), e);\n            throw e;\n        } catch (HttpClientErrorException | HttpServerErrorException e) {\n            log.error(\"Model interface call failed, url={}, http={}, body={}\", url, e.getStatusCode(), e.getResponseBodyAsString(), e);\n            throw new BusinessException(ResponseEnum.MODEL_APIKEY_ERROR);\n        } catch (Exception e) {\n            log.error(\"Model validation failed, url={}, err={}\", url, e.getMessage(), e);\n            throw new BusinessException(ResponseEnum.MODEL_CHECK_FAILED);\n        }\n    }\n\n\n    private String decryptApiKey(String apiKey) {\n        ConfigInfo modelSecretKey = configInfoMapper.selectOne(Wrappers.<ConfigInfo>lambdaQuery()\n                .eq(ConfigInfo::getCategory, \"MODEL_SECRET_KEY\")\n                .eq(ConfigInfo::getCode, \"private_key\")\n                .eq(ConfigInfo::getIsValid, 1));","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java#L148-L184","documentation":"validateModel probes the model endpoint with an OpenAI-compatible request to prove the model works before saving. If the HTTP call completes but the response does not match the accepted OpenAI-compatible shape (and the request was not a pure validation that already saved), the service concludes the endpoint is not OpenAI-compatible and throws MODEL_NOT_COMPATIBLE_OPENAI.","triggerScenarios":"Registering a model whose endpoint answers 200 OK but returns a non-OpenAI response body (different JSON schema, HTML error page, empty body), or a gateway that silently rewrites responses.","commonSituations":"Pointing the endpoint at a non-OpenAI-compatible server (e.g. a plain HTTP page, a vendor API with a different schema); proxy/gateway intercepting and returning its own JSON; custom inference servers (vLLM/old builds) with divergent response fields; wrong path appended to the endpoint URL.","solutions":["Verify the endpoint implements the OpenAI chat/completions response schema (choices[].message.content etc.) — test with curl against the same path the service calls","Check the endpoint URL and path (e.g. /v1/chat/completions) are correct and not hitting an HTML root","Inspect the actual response body logged at validation time to see how it deviates","Use an OpenAI-compatible serving stack (or enable its OpenAI-compatible API mode) for the model"],"exampleFix":"// before\nendpoint: https://llm.internal/  // returns HTML, not OpenAI schema\n// after\nendpoint: https://llm.internal/v1  // proxies to OpenAI-compatible /chat/completions","handlingStrategy":"try-catch","validationCode":"// pre-check OpenAI compatibility before registering\nResponseEntity<String> r = rest.postForEntity(endpoint + \"/chat/completions\", openAiProbeRequest(), String.class);\nif (!r.getStatusCode().is2xxSuccessful() || !r.getBody().contains(\"\\\"choices\\\"\")) throw new IllegalStateException(\"endpoint not OpenAI-compatible\");","typeGuard":"boolean looksOpenAiCompatible(String body) { return body != null && body.trim().startsWith(\"{\") && body.contains(\"\\\"choices\\\"\") && body.contains(\"\\\"message\\\"\"); }","tryCatchPattern":"try { modelService.validateModel(req); } catch (BusinessException e) { if (ResponseEnum.MODEL_NOT_COMPATIBLE_OPENAI.equals(e.getResponseEnum())) { log.error(\"endpoint {} not OpenAI-compatible; inspect response schema\", req.getEndpoint()); return 422-incompatible-endpoint; } throw e; }","preventionTips":["Curl the exact endpoint path and confirm choices[].message shape before registering","Use serving stacks with an official OpenAI-compatible API mode (vLLM, Ollama, etc.)","Beware gateways/proxies that rewrite or wrap response bodies","Verify URL includes the version path (/v1) the compatibility layer expects"],"tags":["openai","api-compatibility","http","validation"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}