iflytek/astron-agent · error · BusinessException
MODEL_NOT_COMPATIBLE_OPENAI
MODEL_NOT_COMPATIBLE_OPENAI
Error message
BusinessException(ResponseEnum.MODEL_NOT_COMPATIBLE_OPENAI)
What it means
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.
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
Example fix
// before endpoint: https://llm.internal/ // returns HTML, not OpenAI schema // after endpoint: https://llm.internal/v1 // proxies to OpenAI-compatible /chat/completions
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check OpenAI compatibility before registering
ResponseEntity<String> r = rest.postForEntity(endpoint + "/chat/completions", openAiProbeRequest(), String.class);
if (!r.getStatusCode().is2xxSuccessful() || !r.getBody().contains("\"choices\"")) throw new IllegalStateException("endpoint not OpenAI-compatible"); Type guard
boolean looksOpenAiCompatible(String body) { return body != null && body.trim().startsWith("{") && body.contains("\"choices\"") && body.contains("\"message\""); } Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/f990f0159eb3b612.
Report an issue: GitHub.
Appendix: source
Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java:166
// 2) Construct/validate URL + request body/headers
final String provider = normalizeProvider(request.getProvider(), true);
final String url = buildModelApiUrlNew(request.getEndpoint(), provider, request.getDomain());
final Map<String, Object> requestBody =
buildValidationPayload(request.getDomain(), provider);
final HttpHeaders headers = buildAuthHeaders(decryptedApiKey, provider);
try {
String responseBody = doPostModelApi(url, requestBody, headers);
if (isValidModelResponse(responseBody, provider)) {
log.info("Model validation passed, domain={}, endpoint={}", request.getDomain(), url);
request.setApiKey(decryptedApiKey);
request.setEndpoint(url);
request.setProvider(provider);
saveOrUpdateModel(request);
return "Model validation passed";
}
throw new BusinessException(ResponseEnum.MODEL_NOT_COMPATIBLE_OPENAI);
} catch (BusinessException e) {
log.error("Model validation failed, url={}, err={}", url, e.getMessage(), e);
throw e;
} catch (HttpClientErrorException | HttpServerErrorException e) {
log.error("Model interface call failed, url={}, http={}, body={}", url, e.getStatusCode(), e.getResponseBodyAsString(), e);
throw new BusinessException(ResponseEnum.MODEL_APIKEY_ERROR);
} catch (Exception e) {
log.error("Model validation failed, url={}, err={}", url, e.getMessage(), e);
throw new BusinessException(ResponseEnum.MODEL_CHECK_FAILED);
}
}
private String decryptApiKey(String apiKey) {
ConfigInfo modelSecretKey = configInfoMapper.selectOne(Wrappers.<ConfigInfo>lambdaQuery()
.eq(ConfigInfo::getCategory, "MODEL_SECRET_KEY")
.eq(ConfigInfo::getCode, "private_key")
.eq(ConfigInfo::getIsValid, 1));View on GitHub (pinned to 5e758547a8)