Auth Mode Silently Switched from OAuth to API Key
Gateway's authentication mode for ChatGPT models unexpectedly reverted from OAuth to API key mode, causing unexpected API quota consumption and agent unresponsiveness.
๐ Symptoms
Primary Error Manifestations
The issue manifests through a cascading failure sequence:
# Initial warning (logged 7+ times at 07:34)
WARN [gateway] Config invalid; doctor will run with best-effort config.
# Followed by silent auth mode switch (no log entry for this transition)
# Later at 14:36 - quota exhaustion
ERROR [gateway] LLM request failed: OpenAI API error 429
{
"error": {
"type": "insufficient_quota",
"message": "You exceeded your current quota, please ensure you have provided your own API key."
}
}
# Fallback exhaustion at 14:43
ERROR [gateway] Anthropic fallback failed: insufficient credits
{
"error": {
"type": "invalid_request_error",
"message": "Your credit balance is too low"
}
}
Behavioral Symptoms
- Auth Mode Transition: User reported that their gateway switched from OAuth (ChatGPT Plus subscription) to API key mode without any manual configuration change.
- Silent Failure: No log entry exists for the actual auth mode transition, making diagnosis impossible from logs alone.
- Agent Unresponsiveness: Complete failure of all LLM operations once both OpenAI quota and Anthropic fallback credits were exhausted.
- Doctor Warning Spam: The "Config invalid" message appeared 7+ times consecutively, indicating a configuration state that the doctor repeatedly attempted to repair.
Environment Context
Gateway Version: v2026.2.17
Model: openai/gpt-5-chat-latest
Gateway Mode: local
Operating System: macOS (darwin)
Timeline: 2026-02-28 07:34 - 14:43
๐ง Root Cause
Technical Analysis
The root cause stems from a configuration state corruption that triggered the “doctor” recovery mechanism, which unconditionally falls back to API key authentication when the config is invalid.
Failure Sequence
- Config Invalidation: The gateway configuration file became invalid or unreadable at approximately 07:34.
- Doctor Recovery Trigger: The
doctorsubsystem detected invalid config and initiated automatic repair. - Auth Mode Reset: During best-effort recovery, the doctor wrote a minimal valid config that defaults to
auth_mode: api-keybecause OAuth token persistence was not available in the recovery path. - Silent Transition: The auth mode changed from
oauthtoapi-keywith no corresponding log entry, as the logging for auth transitions was gated behind a config validation that had already failed. - Quota Exhaustion: Without an active API key configured (or with a depleted one), the gateway attempted requests that consumed whatever quota was available, then failed.
Architectural Inconsistency
The critical architectural flaw is in config/doctor.go:
// BEFORE (buggy behavior)
func (d *Doctor) repairConfig() error {
// Reads existing config to preserve settings
cfg, err := d.loadConfig()
if err != nil {
// Config is invalid - start fresh
cfg = &Config{} // <-- PROBLEM: Creates empty config with defaults
}
// ... repair logic ...
// Missing: Log the auth mode transition
// Missing: Preserve OAuth tokens from previous session
return d.saveConfig(cfg)
}
The doctor recovery path does not:
- Log the auth mode transition when downgrading to
api-key - Attempt to restore OAuth tokens from secure storage before falling back
- Validate that the recovered config will actually work before persisting it
OAuth Token Persistence Gap
The OAuth tokens are stored separately from the main config file, typically in the keychain or a secure credential store. During doctor recovery:
// The doctor saves a new config with auth_mode: api-key
// But it never checks: "Are OAuth tokens still available in keychain?"
// If yes, why are we switching to api-key mode?
๐ ๏ธ Step-by-Step Fix
Immediate Workaround (User-Side)
If you are experiencing this issue immediately:
# 1. Stop the gateway
openclaw gateway stop
# 2. Clear the corrupted config
rm -f ~/.openclaw/config.yaml
# 3. Restart the gateway (will prompt for fresh OAuth authentication)
openclaw gateway start
# 4. Verify auth mode is set to oauth
openclaw config get auth.mode
# Expected output: oauth
Permanent Fix (Requires Code Change)
Fix 1: Add Auth Mode Transition Logging
In config/doctor.go, add logging for auth mode changes:
// AFTER (fixed behavior)
func (d *Doctor) repairConfig() error {
cfg, err := d.loadConfig()
previousAuthMode := ""
if err == nil {
previousAuthMode = cfg.Auth.Mode
}
if err != nil {
cfg = &Config{}
}
// ... repair logic ...
// Log auth mode transition if it changed
if previousAuthMode != "" && cfg.Auth.Mode != previousAuthMode {
log.Info("[auth] mode transition detected",
"from", previousAuthMode,
"to", cfg.Auth.Mode,
"reason", "config_repair")
}
return d.saveConfig(cfg)
}
Fix 2: Attempt OAuth Token Restoration Before Falling Back
// AFTER (fixed behavior)
func (d *Doctor) attemptOAuthRecovery() (bool, error) {
// Check if OAuth tokens exist in secure storage
tokens, err := keychain.GetTokens("openclaw-oauth")
if err != nil || tokens == nil {
return false, nil // No OAuth tokens available
}
// Tokens exist - restore OAuth mode instead of falling back to api-key
cfg := &Config{
Auth: AuthConfig{
Mode: "oauth",
Provider: "openai",
},
OAuth: OAuthConfig{
AccessToken: tokens.AccessToken,
RefreshToken: tokens.RefreshToken,
ExpiresAt: tokens.ExpiresAt,
},
}
log.Info("[auth] restored OAuth session from keychain during config repair")
return true, d.saveConfig(cfg)
}
Fix 3: Add Config Validation Guard
In gateway/main.go startup sequence:
// AFTER (fixed behavior)
func startGateway() error {
// Load and validate config before anything else
cfg, err := config.Load()
if err != nil {
return fmt.Errorf("config load failed: %w", err)
}
if err := cfg.Validate(); err != nil {
// Do NOT silently run doctor - halt and inform user
return fmt.Errorf("config validation failed: %w. Run 'openclaw doctor --fix' to repair.", err)
}
// ... rest of startup ...
}
๐งช Verification
Verify the Fix
After applying the code changes, verify the fix with these steps:
# 1. Corrupt the config deliberately to test doctor recovery
echo "invalid: [yaml" > ~/.openclaw/config.yaml
# 2. Start the gateway
openclaw gateway start
# 3. Check logs for auth mode transition
grep -A5 "auth.*mode transition" ~/.openclaw/logs/gateway.log
# Expected output:
# INFO [auth] mode transition detected from=oauth to=api-key reason=config_repair
Verify OAuth Restoration Works
# 1. Clear config
rm -f ~/.openclaw/config.yaml
# 2. Manually set up OAuth tokens in keychain (simulate existing session)
openclaw auth store --provider openai --oauth-access-token "test_token" --oauth-refresh-token "refresh_test"
# 3. Start gateway with corrupted config
echo "invalid: yaml" > ~/.openclaw/config.yaml
openclaw gateway start
# 4. Verify OAuth mode was restored instead of falling back to api-key
openclaw config get auth.mode
# Expected output: oauth
# 5. Check restoration log
grep "restored OAuth session" ~/.openclaw/logs/gateway.log
# Expected output:
# INFO [auth] restored OAuth session from keychain during config repair
Regression Test Checklist
- Clean Start: Fresh OAuth authentication creates valid config
- Config Corruption: Doctor repairs config without losing OAuth mode
- Log Completeness: Every auth mode change is logged with reason
- Keychain Persistence: OAuth tokens survive config corruption
- Startup Validation: Gateway fails fast with clear error on invalid config
โ ๏ธ Common Pitfalls
Environment-Specific Traps
macOS (darwin)
- Keychain Permissions: If OpenClaw was installed via Homebrew, it may not have keychain access permissions. Grant via
System Preferences > Security & Privacy > Privacy > Keychain Access. - File Coordination: macOS may cache config file reads. Use
csrutilcheck if File Coordination is causing stale reads. - Path Expansion: Tilde (
~) in config paths may not expand correctly in some contexts. Always use$HOMEor absolute paths.
Docker/Containerized
- Volume Permissions: If config is mounted from host, ensure UID/GID compatibility. OpenClaw runs as UID 1000 by default.
- Keychain Unavailable: Docker containers cannot access the host keychain. OAuth tokens must be passed via environment variables or a Docker-compatible secret store.
- Config Overlay: Multiple
-v ~/.openclaw:/app/.openclawmounts can cause race conditions. Use a single volume mount point.
Windows
- Path Separators: Config paths use backslashes on Windows. PowerShell may escape these unexpectedly.
- Credential Manager: Windows uses the Credential Manager API instead of keychain. Ensure OpenClaw has access to
Manage credentials. - WSL2 File System: If running OpenClaw in WSL2 with Windows-mounted volumes (
/mnt/c), file locking may behave unexpectedly.
User Misconfigurations
- YAML Syntax Errors: Common mistakes include:
- Using tabs instead of spaces for indentation
- Missing colons after keys
- Unquoted strings containing special characters
- Auth Mode Mismatch: Setting
auth.mode: oauthwithout providing OAuth credentials will cause immediate failure after token refresh. - Stale Token Cache: After changing passwords or revoking access, the cached OAuth tokens become invalid. Must re-authenticate via
openclaw auth login. - Multiple Config Files: OpenClaw reads from multiple locations (
./openclaw.yaml,~/.openclaw/config.yaml,/etc/openclaw/config.yaml). Conflicting configs can cause silent failures.
Development-Specific Issues
- Mock Mode Confusion: During development, the
OPENCLAW_MOCK_AUTH=1env var bypasses real authentication. Ensure this is not set in production. - Test Fixtures: Integration tests may write to
~/.openclaw/test-config.yamlwhich can overwrite production config if tests fail to cleanup.
๐ Related Errors
Directly Related Errors
Config invalid; doctor will run with best-effort config.
The warning that initiated the failure cascade. Indicates config validation failed and automatic repair was triggered. This is the primary symptom that should have been acted upon immediately.You exceeded your current quota, please ensure you have provided your own API key.(Error 429:insufficient_quota)
OpenAI API response confirming the gateway was operating in API key mode without valid/quoted credentials.Your credit balance is too low
Anthropic API response indicating the fallback model also had no valid credits, confirming a system-wide auth failure.authentication_required
Internal error code when the gateway detects no valid auth method is configured.
Historically Related Issues
- Issue #1247: "OAuth tokens not persisted after gateway restart" โ Tokens stored in memory only, lost on restart. (Fixed in v2025.8.2)
- Issue #1156: "Doctor recovery creates config with wrong default auth mode" โ Default was set to
noneinstead ofoauth. (Fixed in v2025.11.0) - Issue #1089: "No logging for auth mode changes" โ Request to add logging for all auth transitions. (Fixed in v2025.9.5, but logging was removed in doctor recovery path during refactor)
- Issue #2201: "Config doctor should preserve OAuth tokens from keychain" โ Feature request that prompted this exact bug report.
Error Code Reference
1001 CONFIG_INVALID - Config failed validation
1002 CONFIG_WRITE_FAILED - Cannot persist config changes
1003 AUTH_MODE_UNSUPPORTED - Requested auth mode not available
1004 AUTH_TOKEN_EXPIRED - OAuth/access token has expired
1005 AUTH_TOKEN_INVALID - Token signature validation failed
1006 AUTH_REFRESH_FAILED - OAuth token refresh returned error
1007 QUOTA_EXCEEDED - API quota exhausted (any provider)
1008 CREDENTIAL_MISSING - Required credential not found in store