apache/shenyu · error · IllegalArgumentException
baseUrl must not be empty
Error message
baseUrl must not be empty
What it means
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.
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.
Example fix
// before (admin config)
{"aiConfig": {"apiKey":"sk-...", "baseUrl":""}}
// after
{"aiConfig": {"apiKey":"sk-...", "baseUrl":"https://api.openai.com"}} Defensive patterns
Strategy: validation
Validate before calling
function validateAiConfig(cfg) {
if (!cfg || typeof cfg.baseUrl !== 'string' || cfg.baseUrl.trim() === '') {
throw new Error('AI proxy config: baseUrl must not be empty');
}
} Type guard
function hasBaseUrl(cfg) {
return typeof cfg?.baseUrl === 'string' && cfg.baseUrl.trim().length > 0;
} Try / catch
try {
OpenAiApi api = plugin.createOpenAiApi(config);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("AI proxy misconfigured: " + e.getMessage());
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- apiKey must not be empty
- shenyu.jwt.secretKey is not configured. In a multi-instance…
- shenyu discovery mode current didn't support
- websocket on client open failed, namespaceId is null
- websocket sync token is not configured
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/073701c4858a2c89.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java:245
private OpenAiApi getCachedOpenAiApi(final String selectorId, final String type, final AiCommonConfig config) {
final String cacheKey = selectorId + "|" + type + "_" + generateConfigCacheKey(config);
return OpenAiApiCache.getInstance().computeIfAbsent(cacheKey, () -> createOpenAiApi(config));
}
private int generateConfigCacheKey(final AiCommonConfig config) {
return Objects.hash(
config.getBaseUrl(),
config.getApiKey(),
config.getModel(),
config.getTemperature(),
config.getMaxTokens()
);
}
private OpenAiApi createOpenAiApi(final AiCommonConfig config) {
if (Objects.isNull(config.getBaseUrl()) || config.getBaseUrl().isEmpty()) {
throw new IllegalArgumentException("baseUrl must not be empty");
}
if (Objects.isNull(config.getApiKey()) || config.getApiKey().isEmpty()) {
throw new IllegalArgumentException("apiKey must not be empty");
}
return OpenAiApi.builder()
.baseUrl(config.getBaseUrl())
.apiKey(config.getApiKey())
.build();
}
@Override
public int getOrder() {
return PluginEnum.AI_PROXY.getCode();
}
@Override
public String named() {
return PluginEnum.AI_PROXY.getName();View on GitHub (pinned to 567142e072)