-
-
Notifications
You must be signed in to change notification settings - Fork 69.5k
Setup wizard persists Symbol(clack:cancel) as token when user cancels manual token entry #38928
Description
Bug Report
Summary
When the setup wizard prompts for a manual API token and the user cancels the prompt (Ctrl+C / Escape), the clack library's cancel symbol (Symbol(clack:cancel)) is coerced to the string "Symbol(clack:cancel)" and written to auth-profiles.json as the token value. The profile entry persists with this bogus string rather than being discarded.
Behavior
In auth-profiles.json, the resulting entry looks like:
"openai:manual": {
"type": "token",
"provider": "openai",
"token": "Symbol(clack:cancel)"
}Impact
If openclaw ever selects this profile as a fallback (e.g., when a primary auth provider is temporarily unavailable), it sends Symbol(clack:cancel) as the Bearer token and receives a 401:
openai embeddings failed: 401 {
"error": {
"message": "Incorrect API key provided: Symbol(c********cel).",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
In our case: BWS (Bitwarden Secrets) was temporarily degraded → Gemini embedding auth failed → openclaw fell back to openai:manual → 401 from OpenAI embeddings API.
Expected Behavior
When the user cancels token entry during the wizard, the profile should either:
- Not be written to
auth-profiles.jsonat all, or - Be written with
token: nulland skipped when selected (no 401 attempt)
Steps to Reproduce
- Run
openclaw configure(or equivalent wizard) - When prompted to enter a manual API token for any provider, press Ctrl+C or Escape
- Check
~/.openclaw/agents/<agent>/agent/auth-profiles.json - The profile entry will have
"token": "Symbol(clack:cancel)"
Environment
- OpenClaw version: 2026.3.2
- macOS Darwin 24.3.0 arm64
Workaround
Manually null out the token in all affected auth-profiles.json files:
import json, glob
for f in glob.glob('/path/to/.openclaw/agents/*/agent/auth-profiles.json'):
with open(f) as fh: data = json.load(fh)
for profile in data.get('profiles', {}).values():
if profile.get('token') == 'Symbol(clack:cancel)':
profile['token'] = None
with open(f, 'w') as fh: json.dump(data, fh, indent=2)