fix(models): keep bundled provider catalog when configured base URL is blank (#91270)#91292
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 7, 2026, 9:36 PM ET / 01:36 UTC. Summary PR surface: Source +20. Total +20 across 1 file. Reproducibility: yes. The source path on current main shows blank explicit provider Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the narrow planner fix after maintainers confirm blank provider Do we have a high-confidence way to reproduce the issue? Yes. The source path on current main shows blank explicit provider Is this the best way to solve the issue? Yes, with maintainer confirmation. Stripping blank AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 3ffb3609a141. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +20. Total +20 across 1 file. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…s blank
A models.providers.<id> entry with a blank baseUrl ("") erased the
bundled provider catalog from the generated model registry. The empty
base URL flowed into provider discovery and the catalog merge, where it
overrode the bundled transport URL; the resulting provider then failed
isWritableProviderConfig and was dropped from models.json entirely.
For Google this meant gemini-2.5-flash and gemini-flash-latest stopped
resolving on the embedded runtime with model_not_found, even though the
bundled Google catalog and forward-compat resolver know those ids. This
hit users in merge mode whose config was partially written by a plugin.
Strip blank provider base URLs before discovery and merge so a blank
value means "use the provider default" instead of clobbering it.
Fixes openclaw#91270
5a5c8bf to
c71a867
Compare
|
Maintainer verification complete.
|
…s blank (openclaw#91270) (openclaw#91292) * fix(models): keep bundled provider catalog when configured base URL is blank A models.providers.<id> entry with a blank baseUrl ("") erased the bundled provider catalog from the generated model registry. The empty base URL flowed into provider discovery and the catalog merge, where it overrode the bundled transport URL; the resulting provider then failed isWritableProviderConfig and was dropped from models.json entirely. For Google this meant gemini-2.5-flash and gemini-flash-latest stopped resolving on the embedded runtime with model_not_found, even though the bundled Google catalog and forward-compat resolver know those ids. This hit users in merge mode whose config was partially written by a plugin. Strip blank provider base URLs before discovery and merge so a blank value means "use the provider default" instead of clobbering it. Fixes openclaw#91270 * test(models): cover blank provider base url --------- Co-authored-by: Vincent Koc <[email protected]>
…s blank (openclaw#91270) (openclaw#91292) * fix(models): keep bundled provider catalog when configured base URL is blank A models.providers.<id> entry with a blank baseUrl ("") erased the bundled provider catalog from the generated model registry. The empty base URL flowed into provider discovery and the catalog merge, where it overrode the bundled transport URL; the resulting provider then failed isWritableProviderConfig and was dropped from models.json entirely. For Google this meant gemini-2.5-flash and gemini-flash-latest stopped resolving on the embedded runtime with model_not_found, even though the bundled Google catalog and forward-compat resolver know those ids. This hit users in merge mode whose config was partially written by a plugin. Strip blank provider base URLs before discovery and merge so a blank value means "use the provider default" instead of clobbering it. Fixes openclaw#91270 * test(models): cover blank provider base url --------- Co-authored-by: Vincent Koc <[email protected]>
Summary
Fixes #91270. On the embedded runtime, Google Gemini models stop resolving with
model_not_found:even though the bundled Google catalog and forward-compat resolver know
gemini-2.5-flashandgemini-flash-latest. The reporter hit this withmodels.mode: "merge"and a config edited by a plugin; their workaround was to add explicitmodels.providers.google.models[]rows.Root cause
When a
models.providers.<id>entry exists with a blankbaseUrl: ""(as written by a plugin or a partial hand edit), that empty string flows into models.json planning:buildPluginCatalogConfig) and the catalog merge, where it overrides the bundled transport base URL, so the discovered Google catalog ends up withbaseUrl: "".isWritableProviderConfigthen treats the provider as non-writable (baseUrl?.trim() && apiKeyis false), sofilterWritableProvidersdrops the whole Google provider from the generated registry, including its bundled catalog and forward-compat templates.gemini-2.5-flash) and the forward-compat clone (gemini-flash-latest, which clones agemini-2.5-flashtemplate) both fail, so resolution falls through to themodel_not_foundregistration hint.A blank
baseUrlshould mean "use the provider default", not "erase the bundled catalog".Fix
resolveProvidersForModelsJsonWithDepsstrips blank providerbaseUrlvalues from the explicit config before they reach discovery and the merge, so the bundled catalog base URL survives and the provider stays writable. The change is provider-agnostic.Real behavior proof
Behavior addressed: Embedded runtime fails to resolve
google/gemini-2.5-flash/google/gemini-flash-latestwithmodel_not_foundwhen amodels.providers.googleentry exists with a blankbaseUrland no/partialmodels[](merge mode, plugin-edited config).Real environment tested: Local checkout at
origin/main5c53918, Node 22.19,GEMINI_API_KEYset; drove the productionensureOpenClawModelsJsonthenresolveModelAsync("google", id, ...)decision path for the reporter-shaped config across three variants (C1 no explicit google provider; C2 explicit google with model rows; C3 explicit google{ api, baseUrl: "" }with no model rows).Exact steps or command run after this patch: generated models.json then resolved
google/gemini-2.5-flashandgoogle/gemini-flash-latestfor each of C1, C2, C3 through the real resolver decision path.Evidence after fix: copied live terminal output from the real resolver decision path.
Before the fix (C3 = blank-baseUrl plugin-written google entry):
After the fix:
Observed result after fix: all three configs resolve both Gemini ids on the embedded runtime; C1 and C2 were already resolving and stayed green (no regression).
What was not tested: a live request against the Google Gemini API (resolution only, no network call); non-Google providers were not exercised live, though the fix is provider-agnostic.
Verification
Local (Node 22.19.0):
pnpm tsgo:coreexit 0;oxfmt --check src/agents/models-config.plan.tsclean; type-aware oxlint on the changed file clean.Related (not duplicates)
{ "id": ... }butnameis required andapi/baseUrlare silently needed (misroutes to OpenAI) #89192 (incompletemodel_not_foundremediation message) are adjacent issues in the same provider-config cluster. Keeping the bundled catalog whenbaseUrlis blank helps that cluster but does not fully cover their separate asks.