srbhr/Resume-Matcher · error · HTTPException
missing_base_url
missing_base_url
Error message
missing_base_url
What it means
A structured 422 validation error with detail {code:'missing_base_url', field:'api_base', missing:['api_base']} raised by update_llm_config. The selected provider is in PROVIDERS_REQUIRING_BASE_URL (providers that need an explicit API base endpoint) but no api_base is stored or derivable, so the config would be unusable.
Source
Thrown at apps/backend/app/routers/config.py:181
if request.reasoning_effort is not None:
# Persist empty string on clear so the gpt-5 auto-migration doesn't
# re-fire on next get_llm_config() call.
stored["reasoning_effort"] = request.reasoning_effort
# Build normalized config for response and background health check
resolved_provider = stored.get("provider", settings.llm_provider)
# M-05: `requiresBaseUrl` was enforced in the settings UI only, so the
# .env-driven path could persist a provider that cannot work without an
# endpoint. Fail at save time with a field name instead of surfacing an
# opaque LiteLLM error on the user's first generation.
if resolved_provider in PROVIDERS_REQUIRING_BASE_URL and not (
_effective_api_base(stored)
):
# Structured detail using the same {code, field, missing} shape as
# update_feature_prompts below, so the UI has one schema to read for
# every validation error out of this router.
raise HTTPException(
status_code=422,
detail={
"code": "missing_base_url",
"field": "api_base",
"missing": ["api_base"],
},
)
raw_re = stored.get("reasoning_effort", settings.reasoning_effort)
resolved_reasoning_effort = raw_re if raw_re else None
test_config = LLMConfig(
provider=resolved_provider,
model=stored.get("model", settings.llm_model),
api_key=resolve_api_key(stored, resolved_provider),
api_base=_effective_api_base(stored),
reasoning_effort=resolved_reasoning_effort,
)
# Save config regardless of health check outcome (see docstring).View on GitHub (pinned to 116f9cc3b0)
Solutions
- Provide api_base in the request payload or fill the API base URL field in the UI
- Set the environment/config value that supplies the base for the provider
- Choose a provider that does not require a base URL if using a hosted default
Example fix
// before
await api.updateLlmConfig({ provider: 'custom' })
// after
await api.updateLlmConfig({ provider: 'custom', api_base: 'https://llm.example.com/v1' }) Defensive patterns
Strategy: validation
Validate before calling
const REQUIRES_BASE = ['custom','self-hosted','openai-compatible']
if (REQUIRES_BASE.includes(cfg.provider) && !cfg.api_base) {
throw new Error('provider ' + cfg.provider + ' requires api_base')
} Try / catch
try {
await api.updateLlmConfig(cfg)
} catch (e) {
const d = e.response?.data?.detail
if (d?.code === 'missing_base_url') {
highlightField('api_base', 'This provider requires an API base URL')
} else throw e
} Prevention
- Require the api_base field in the UI when a base-URL provider is selected
- Check the structured detail.code to route validation errors to the right field
- Keep base URLs in env/config so they survive provider switches
- Test config saves after provider migrations
When it happens
Trigger: PUT/POST the LLM config selecting a provider that requires a base URL (e.g. custom/self-hosted/OpenAI-compatible providers) while the api_base field is empty and no effective base can be resolved from stored config or environment.
Common situations: User picks a custom provider in the settings UI but leaves the API base URL blank; migrating configs between environments where the env var supplying the base URL is unset.
Related errors
- missing_placeholders
- missing_placeholders
- ${message = data.detail or Failed to update LLM config (stat
- ${data.detail || Failed to update feature config (status ${r
- ${data.detail || Failed to update language config (status ${
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/f7de8f200c215122.
Report an issue: GitHub.