--- id: provider-switch-api-protocol-validation title: Reject invalid OpenClaw api protocol writes and self-heal stale entries scenario: gateway-backend-communication taskType: runtime-bridge intent: Prevent ClawX from writing models.providers.*.api values outside the OpenClaw allowlist into openclaw.json, and opportunistically prune pre-existing invalid entries when the user switches default provider so a clean switch can rescue the Gateway from Invalid-config restart loops. touchedAreas: - harness/specs/tasks/provider-switch-api-protocol-validation.md - electron/shared/providers/registry.ts - electron/shared/providers/types.ts - electron/utils/openclaw-auth.ts - electron/services/providers/provider-runtime-sync.ts - tests/unit/openclaw-auth.test.ts expectedUserBehavior: - Selecting OpenRouter (or any built-in provider) as default writes a Gateway-valid api protocol so Gateway reload stays healthy. - Attempting to save a provider whose api protocol is not in the OpenClaw allowlist surfaces an actionable error in the UI without polluting openclaw.json or triggering a Gateway restart. - Switching default to a healthy provider after a prior corrupt openclaw.json (legacy api=openrouter typo) self-heals the file before the next reload signal so the Gateway boots cleanly. requiredProfiles: - fast - comms requiredRules: - active-config-guards - backend-communication-boundary - renderer-main-boundary - api-client-transport-policy requiredTests: - tests/unit/openclaw-auth.test.ts acceptance: - electron/shared/providers/types.ts exports OPENCLAW_API_PROTOCOLS as the single source of truth for openclaw.json api values, plus assertValidApiProtocol and InvalidApiProtocolError. - setOpenClawDefaultModel, setOpenClawDefaultModelWithOverride, syncProviderConfigToOpenClaw, and upsertOpenClawProviderEntry all invoke assertValidApiProtocol before any write to openclaw.json. - syncDefaultProviderToRuntime calls pruneInvalidApiProviderEntries before the OAuth and non-OAuth branches so a switch to any healthy provider drops legacy invalid models.providers entries. - Renderer does not add new direct ipcRenderer or Gateway HTTP calls. - Unit tests cover both the rejection path (no file write on invalid api) and the self-heal path (legacy entries pruned while valid ones remain). docs: required: false --- ## Background A historical bug in [electron/shared/providers/registry.ts](electron/shared/providers/registry.ts) set the OpenRouter `providerConfig.api` to the literal string `'openrouter'`, which is not in OpenClaw's allowed `api` enum (`openai-completions | openai-responses | openai-codex-responses | anthropic-messages | google-generative-ai | github-copilot | bedrock-converse-stream | ollama | azure-openai-responses`). When the user selected OpenRouter as their default provider, ClawX wrote that invalid value into `~/.openclaw/openclaw.json` and then sent SIGUSR1 to the running Gateway. The Gateway's `assertValidGatewayStartupConfigSnapshot` rejected the config, tore down all channels (close code 1012), entered the `startup_failed` state, but kept the OS process alive. The Main process then spun for ~8 minutes inside `waitForGatewayReady` before doctor self-heal kicked in. Switching to MiniMax during the broken window did not help, because the runtime-sync path only appended the MiniMax block and left the broken OpenRouter entry untouched. ## Scope - Fix the registry typo so OpenRouter writes `api: 'openai-completions'`, matching every other OpenAI-compatible built-in provider. - Add a single source of truth for the allowed api protocols and a runtime guard at every write site (`setOpenClawDefaultModel`, `setOpenClawDefaultModelWithOverride`, `syncProviderConfigToOpenClaw`, plus defense-in-depth inside `upsertOpenClawProviderEntry`). - Add a self-heal helper that prunes any `models.providers.*` entry whose `api` is outside the allowlist, and invoke it at the top of `syncDefaultProviderToRuntime` so a clean switch rescues legacy corrupted configs. ## Out of scope - Gateway-side `waitForGatewayReady` short-circuit when the owned process is stuck in `startup_failed`. - Renderer pre-flight validation UI surface beyond the existing route-level error handling. - README updates (no user-visible UI change in this task).