{"record":{"id":"073701c4858a2c89","repo":"apache/shenyu","slug":"baseurl-must-not-be-empty","errorCode":null,"errorMessage":"baseUrl must not be empty","messagePattern":"baseUrl 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":245,"sourceCode":"\n    private OpenAiApi getCachedOpenAiApi(final String selectorId, final String type, final AiCommonConfig config) {\n        final String cacheKey = selectorId + \"|\" + type + \"_\" + generateConfigCacheKey(config);\n        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();","sourceCodeStart":227,"sourceCodeEnd":263,"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#L227-L263","documentation":"AiProxyPlugin.createOpenAiApi builds an OpenAiApi client from the plugin's AiCommonConfig and requires a non-null, non-empty baseUrl. When the configured AI provider base URL is missing the plugin throws IllegalArgumentException instead of constructing a client that could not reach any upstream.","triggerScenarios":"The AI proxy selector/rule config (AiCommonConfig.baseUrl) is null or empty string when createOpenAiApi is invoked from getCachedOpenAiApi or resolveFallbackContext (e.g. on a cache miss or fallback resolution).","commonSituations":"Operator created the selector/rule in the ShenYu dashboard but left the baseUrl field blank; config sync delivered a partially filled AiCommonConfig; switching providers and clearing the URL without re-saving.","solutions":["Set baseUrl in the AI proxy plugin config (dashboard selector/rule or matching config source) to the provider endpoint, e.g. https://api.openai.com.","Re-publish the plugin config so the gateway cache picks up the corrected value.","Add a config validation step in admin/publishing that rejects empty baseUrl before it reaches the gateway.","Check resolveFallbackContext's fallback config too — a valid primary config with an empty fallback baseUrl still triggers this."],"exampleFix":"// before (admin config)\n{\"aiConfig\": {\"apiKey\":\"sk-...\", \"baseUrl\":\"\"}}\n// after\n{\"aiConfig\": {\"apiKey\":\"sk-...\", \"baseUrl\":\"https://api.openai.com\"}}","handlingStrategy":"validation","validationCode":"function validateAiConfig(cfg) {\n  if (!cfg || typeof cfg.baseUrl !== 'string' || cfg.baseUrl.trim() === '') {\n    throw new Error('AI proxy config: baseUrl must not be empty');\n  }\n}","typeGuard":"function hasBaseUrl(cfg) {\n  return typeof cfg?.baseUrl === 'string' && cfg.baseUrl.trim().length > 0;\n}","tryCatchPattern":"try {\n  OpenAiApi api = plugin.createOpenAiApi(config);\n} catch (IllegalArgumentException e) {\n  throw new IllegalStateException(\"AI proxy misconfigured: \" + e.getMessage());\n}","preventionTips":["Make baseUrl a required field in the admin dashboard form for AI proxy rules.","Validate plugin config on publish in shenyu-admin before syncing to gateways.","Pin the provider endpoint per environment in config templates."],"tags":["configuration","ai-proxy","missing-value"],"backgroundTag":"empty-required-field","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"}