Summary
When OAuth tokens are renewed via openclaw onboard, the token is saved to auth-profiles.json (live copy) but NOT to auth-profiles.provisioned.json (golden copy). This causes safe-mode recovery to repeatedly fail because it reads from the golden copy.
The Architecture
~/.openclaw/agents/main/agent/auth-profiles.json # Live copy (gateway reads/writes)
~/.openclaw/agents/main/agent/auth-profiles.provisioned.json # Golden copy (recovery reads)
From build-full-config.sh (lines 275-279):
# Golden copy — never modified by gateway, survives shutdown persistence clobber.
# Safe mode recovery reads from this to find OAuth tokens that may have been
# dropped from the live copy during gateway restart cycles.
# NOTE: This is static after provisioning. If credentials are rotated at runtime
# (e.g., OAuth token refresh), re-run build-full-config.sh to update the golden copy.
From safe-mode-recovery.sh (lines 192-200):
# Find auth-profiles.json — prefer golden provisioned copy (survives gateway
# SIGTERM persistence clobber), fall back to live copy if golden doesn't exist.
for path in \
"$home/.openclaw/agents/main/agent/auth-profiles.provisioned.json" \
"$home/.openclaw/agents/main/agent/auth-profiles.json" \
...
The Problem
- User runs
openclaw onboard --auth-choice openai-codex
- OAuth flow completes, token saved to
auth-profiles.json ✅
auth-profiles.provisioned.json is NOT updated ❌
- Later, something triggers safe-mode recovery
- Recovery reads from
.provisioned.json (stale token)
- Recovery thinks OAuth is broken → stays in safe mode
- User re-runs OAuth → same loop repeats
Current Workaround
After OAuth renewal, manually copy live → golden:
cp ~/.openclaw/agents/main/agent/auth-profiles.json \
~/.openclaw/agents/main/agent/auth-profiles.provisioned.json
Proposed Fix
Option A: Update openclaw onboard to sync golden copy
When openclaw onboard successfully saves OAuth credentials, also update the provisioned copy:
// After writing auth-profiles.json
const provisionedPath = path.join(agentDir, 'auth-profiles.provisioned.json');
if (fs.existsSync(provisionedPath)) {
fs.copyFileSync(authProfilesPath, provisionedPath);
}
Option B: Document the manual step
Add to safe-mode agent TOOLS.md and main docs:
- Explain the two-file architecture
- Document the manual copy step after OAuth renewal
Context
This was discovered during a multi-hour safe-mode debugging session where:
- Discord tokens were fixed in both
habitat-parsed.env and openclaw.full.json
- OpenAI OAuth was renewed multiple times via
openclaw onboard
- Recovery kept reverting to safe mode because
.provisioned.json had stale token
Related issue: #37659 (other safe-mode documentation gaps)
Summary
When OAuth tokens are renewed via
openclaw onboard, the token is saved toauth-profiles.json(live copy) but NOT toauth-profiles.provisioned.json(golden copy). This causes safe-mode recovery to repeatedly fail because it reads from the golden copy.The Architecture
From
build-full-config.sh(lines 275-279):From
safe-mode-recovery.sh(lines 192-200):The Problem
openclaw onboard --auth-choice openai-codexauth-profiles.json✅auth-profiles.provisioned.jsonis NOT updated ❌.provisioned.json(stale token)Current Workaround
After OAuth renewal, manually copy live → golden:
Proposed Fix
Option A: Update
openclaw onboardto sync golden copyWhen
openclaw onboardsuccessfully saves OAuth credentials, also update the provisioned copy:Option B: Document the manual step
Add to safe-mode agent TOOLS.md and main docs:
Context
This was discovered during a multi-hour safe-mode debugging session where:
habitat-parsed.envandopenclaw.full.jsonopenclaw onboard.provisioned.jsonhad stale tokenRelated issue: #37659 (other safe-mode documentation gaps)