{"record":{"id":"35048eb1f88fba63","repo":"apache/shenyu","slug":"apikey-must-not-be-empty","errorCode":null,"errorMessage":"apiKey must not be empty","messagePattern":"apiKey must not be empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java","lineNumber":248,"sourceCode":"        return OpenAiApiCache.getInstance().computeIfAbsent(cacheKey, () -> createOpenAiApi(config));\n    }\n\n    private int generateConfigCacheKey(final AiCommonConfig config) {\n        return Objects.hash(\n                config.getBaseUrl(),\n                config.getApiKey(),\n                config.getModel(),\n                config.getTemperature(),\n                config.getMaxTokens()\n        );\n    }\n\n    private OpenAiApi createOpenAiApi(final AiCommonConfig config) {\n        if (Objects.isNull(config.getBaseUrl()) || config.getBaseUrl().isEmpty()) {\n            throw new IllegalArgumentException(\"baseUrl must not be empty\");\n        }\n        if (Objects.isNull(config.getApiKey()) || config.getApiKey().isEmpty()) {\n            throw new IllegalArgumentException(\"apiKey must not be empty\");\n        }\n        return OpenAiApi.builder()\n                .baseUrl(config.getBaseUrl())\n                .apiKey(config.getApiKey())\n                .build();\n    }\n\n    @Override\n    public int getOrder() {\n        return PluginEnum.AI_PROXY.getCode();\n    }\n\n    @Override\n    public String named() {\n        return PluginEnum.AI_PROXY.getName();\n    }\n\n    private void logUpstreamError(final Throwable e, final String mode) {","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java#L230-L266","documentation":"createOpenAiApi in AiProxyPlugin also requires a non-null, non-empty apiKey to authenticate with the AI provider. If the key is absent the plugin throws IllegalArgumentException before building the OpenAiApi client.","triggerScenarios":"AiCommonConfig.apiKey is null or empty when createOpenAiApi runs (initial client cache build via getCachedOpenAiApi, or fallback client creation via resolveFallbackContext).","commonSituations":"Admin config saved without the API key, secret stripped by a templating/deployment pipeline, key rotated and removed from config, or a different environment (staging) lacking the credential.","solutions":["Set apiKey in the AI proxy plugin configuration to a valid provider credential and re-save/publish.","Verify the secret actually reaches the gateway config store (check what the dashboard saved and what data sync delivered).","Inject the key from a secrets manager/environment template if it is being lost during deployment.","Validate both primary and fallback AiCommonConfig, since resolveFallbackContext uses a separate config object."],"exampleFix":"// before\n{\"baseUrl\":\"https://api.openai.com\"} // apiKey missing\n// after\n{\"baseUrl\":\"https://api.openai.com\",\"apiKey\":\"${OPENAI_API_KEY}\"}","handlingStrategy":"validation","validationCode":"function validateAiConfig(cfg) {\n  if (!cfg || typeof cfg.apiKey !== 'string' || cfg.apiKey.trim() === '') {\n    throw new Error('AI proxy config: apiKey must not be empty');\n  }\n}","typeGuard":"function hasApiKey(cfg) {\n  return typeof cfg?.apiKey === 'string' && cfg.apiKey.trim().length > 0;\n}","tryCatchPattern":"try {\n  OpenAiApi api = plugin.createOpenAiApi(config);\n} catch (IllegalArgumentException e) {\n  log.error(\"AI apiKey missing\", e);\n  return ResponseEntity.status(503).body(\"AI proxy not configured\");\n}","preventionTips":["Inject the API key from a secrets manager rather than hand-editing config.","Verify secret presence in CI before deploying gateway config.","Keep primary and fallback AiCommonConfig both populated."],"tags":["configuration","ai-proxy","credentials"],"backgroundTag":"missing-api-key","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}