john-rood/claude-proxy/anthropic-sub-proxy

Claude Code and agent skill by john-rood/claude-proxy

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Troubleshoot and configure the Anthropic Subscription Proxy for OpenClaw. Use when the user has "The AI service returned an error", 401 auth failures through the proxy, key routing issues, pi-ai baseUrl overwrites, or needs help setting up the Claude Code proxy with OpenClaw. Also use when the proxy is not receiving requests, or requests bypass the proxy entirely.

Install

npx vskill@latest install john-rood/claude-proxy/anthropic-sub-proxy

SKILL.md

Anthropic Subscription Proxy — Troubleshooting

The proxy is a lightweight Node.js server (~150 lines, zero dependencies) that sits between OpenClaw and api.anthropic.com. It adds the Claude Code envelope (beta header + system prompt) to requests so Claude Code subscription billing applies instead of direct API billing.

Proxy repo: Separate from this skill. The user should already have index.js and config.json deployed.

How It Works

OpenClaw → localhost:<port> → Proxy → api.anthropic.com
↓
1. Pass through client's API key unchanged
2. Add anthropic-beta: claude-code-20250219
3. Prepend "You are Claude Code..." system prompt
4. Stream response back untouched

The proxy is passthrough by default — it forwards whatever API key the client sends. No keys need to be configured in the proxy itself.

Critical: How Keys Actually Work

The only key that reliably works

The key managed by Claude Code CLI in the macOS Keychain (sk-ant-api03-...). This is the key tied to whichever account is currently logged into Claude Code CLI. It gets auto-synced into OpenClaw's auth system at runtime, regardless of what's in config files.

setup-token keys (sk-ant-oat01-...) are unreliable

Tokens generated via claude setup-token consistently arrive invalid (401 invalid x-api-key) even when generated moments before testing. The reason is unknown — they may not be activating server-side, or they may require a different auth flow. Do not rely on these for the proxy.

What this means
  • One subscription at a time. Claude Code CLI only stores one account's credentials. The proxy can only use that one key.
  • auth-profiles.json is misleading. OpenClaw's auth system auto-syncs from the Keychain on every load, overriding whatever is manually configured. The token Claudius actually sends may not match any named profile in the config.
  • Round-robin across multiple subscriptions is not currently possible unless multiple valid sk-ant-api03 keys can be captured from separate Claude Code CLI sessions (which requires logging in and out — and logging out may revoke the previous key).
To identify which key is actually being used

Check the runtime auth log, not the config files:

bash
grep "ANTHROPIC-AUTH" <openclaw-log> | tail -5

The tokenPrefix field shows what's actually being sent. Compare against:

  • Config keys in auth-profiles.json
  • Keychain-synced keys (auto-injected, no profile name)

If the token prefix doesn't match any configured profile, it's coming from the Keychain sync.

Diagnostic Checklist

When the user reports errors, run through these checks in order:

1. Is the proxy running?
bash
lsof -iTCP:<port> -sTCP:LISTEN -P
curl -s http://127.0.0.1:<port>/health

If not running, start it:

bash
cd <proxy-dir> && nohup node index.js >> proxy.log 2>&1 &
2. Is OpenClaw actually hitting the proxy?
bash
curl -s http://127.0.0.1:<port>/status
# Check requestsServed — if 0 after sending a message, requests aren't reaching the proxy

Also check the proxy log:

bash
tail -20 <proxy-dir>/proxy.log
3. Is the baseUrl override being respected?

This is the #1 issue. OpenClaw's pi-ai model catalog (models.generated.js) hardcodes baseUrl for every model, overriding whatever is in the OpenClaw config.

Find and patch the model catalog:

bash
# Find the file
find <openclaw-install-dir> -name "models.generated.js" -path "*/pi-ai/*"
# Check current Anthropic baseUrls
grep "baseUrl" <path-to-models.generated.js> | sort -u
# Patch all Anthropic models to point at the proxy
sed -i '' 's|https://api.anthropic.com|http://127.0.0.1:<port>|g' <path-to-models.generated.js>
# Verify
grep -c "127.0.0.1:<port>" <path-to-models.generated.js>

⚠️ This patch gets wiped on OpenClaw updates/reinstalls. Re-run after any update.

4. Auth token issues (401 errors)

If the proxy returns 401 or Anthropic returns 401:

Check what token OpenClaw is actually sending — look for ANTHROPIC-AUTH in OpenClaw's logs:

bash
grep "ANTHROPIC-AUTH" <openclaw-log> | tail -5

Common problems:

a. OpenClaw syncs tokens from Claude Code's Keychain

OpenClaw's auth system auto-syncs credentials from Claude Code CLI's macOS Keychain on every load. This overrides whatever is in auth-profiles.json. If old/expired tokens exist in the Keychain, OpenClaw will use them.

Fix: The proxy should NOT validate client keys. Ensure the proxy accepts any key (passthrough mode). The proxy's job is only to add the envelope, not to authenticate.

b. Multiple auth profiles cycling

If OpenClaw has multiple Anthropic auth profiles (manual, manual2, manual3, default), it round-robins through them. Some may be dead/expired.

Check config:

bash
# Show auth order
cat <openclaw-config> | python3 -c "
import json, sys
cfg = json.load(sys.stdin)
order = cfg.get('auth',{}).get('order',{}).get('anthropic',[])
print('Auth order:', order)
profiles = cfg.get('auth',{}).get('profiles',{})
for name, p in profiles.items():
if 'anthropic' in name:
print(f' {name}: mode={p.get(\"mode\",\"?\")}')"

Fix: Either ensure all profiles have valid keys, or trim the auth order to only valid profiles.

c. OAuth tokens expire

OAuth tokens (sk-ant-oat01-...) expire and need refresh. If refresh fails, the request fails.

Fix: Remove dead OAuth profiles from config and auth-profiles.json. Keep only profiles with valid, non-expired credentials.

5. Proxy returns 502 (upstream error)

The proxy connected to Anthropic but got an error. Check:

  • Is the API key valid? Test directly: curl -s https://api.anthropic.com/v1/messages -H "x-api-key: <key>" -H "anthropic-version: 2023-06-01" -H "content-type: application/json" -d '{"model":"claude-sonnet-4-20250514","max_tokens":10,"messages":[{"role":"user","content":"hi"}]}'
  • Does the key's org have overage/extra usage enabled?
  • Is the account suspended or rate-limited?
6. Proxy works but response is wrong/truncated

Check if streaming is passing through correctly:

bash
# Test streaming
curl -s http://127.0.0.1:<port>/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: <valid-key>" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-sonnet-4-20250514","max_tokens":50,"stream":true,"messages":[{"role":"user","content":"Say hi"}]}'

Quick Validation Test

Run this end-to-end test to confirm the full chain works:

bash
# 1. Health check
curl -s http://127.0.0.1:<port>/health
# 2. Non-streaming test
curl -s http://127.0.0.1:<port>/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: <valid-anthropic-key>" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-sonnet-4-20250514","max_tokens":50,"messages":[{"role":"user","content":"Say hi in 5 words"}]}'
# 3. Check it was logged
tail -5 <proxy-dir>/proxy.log

Persistent Service (macOS launchd)

The proxy should run as a persistent macOS service so it auto-restarts on crash and starts on boot. Without this, the proxy will eventually die and OpenClaw will get connection errors.

Create the plist

Write to ~/Library/LaunchAgents/com.claude-proxy.plist:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.claude-proxy</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/node</string>
<string>/path/to/claude-proxy/index.js</string>
</array>
<key>WorkingDirectory</key>
<string>/path/to/claude-proxy</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>StandardOutPath</key>
<string>/tmp/claude-proxy-stdout.log</string>
<key>StandardErrorPath</key>
<string>/tmp/claude-proxy-stderr.log</string>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
</dict>
</dict>
</plist>

Replace /path/to/node with the actual node binary (e.g. which node or /opt/homebrew/Cellar/node/<version>/bin/node).

Load and verify
bash
launchctl load ~/Library/LaunchAgents/com.claude-proxy.plist
sleep 3
launchctl list | grep claude-proxy # Should show PID and exit code 0
curl -s http://127.0.0.1:<port>/health
Test auto-restart
bash
# Kill the proxy
kill $(lsof -iTCP:<port> -sTCP:LISTEN -P -t)
sleep 6
# Verify it came back with a new PID
lsof -iTCP:<port> -sTCP:LISTEN -P
Common launchd issues
  • Exit code 78 ("Function not implemented"): Node binary path is wrong or launchd can't find it. Use the full absolute path to the node binary, not a symlink. Add PATH in EnvironmentVariables.
  • KeepAlive with `true`: Can cause aggressive restart loops. Use SuccessfulExit: false instead — this only restarts on crashes, not clean exits.
  • Logs: Check /tmp/claude-proxy-stdout.log and /tmp/claude-proxy-stderr.log for startup errors.
Manage the service
bash
# Stop
launchctl unload ~/Library/LaunchAgents/com.claude-proxy.plist
# Restart
launchctl unload ~/Library/LaunchAgents/com.claude-proxy.plist && \
launchctl load ~/Library/LaunchAgents/com.claude-proxy.plist
# Check status
launchctl list | grep claude-proxy

After OpenClaw Updates

OpenClaw updates wipe the models.generated.js patch. After any update:

  1. Re-run the sed patch from step 3
  2. Restart OpenClaw
  3. Verify with a test message

⚠️ Lesson Learned: Key Configuration (2026-04-11)

The canonical key store is `auth-profiles.json`, NOT env vars.

When setting up or changing Anthropic keys for the proxy:

DO THIS FIRST
bash
# Find the auth-profiles file
find ~/.openclaw -name "auth-profiles.json" -not -path "*/browser/*"
# → ~/.openclaw/agents/main/agent/auth-profiles.json
# Read it — this is where the actual key lives
cat ~/.openclaw/agents/main/agent/auth-profiles.json
Edit the key directly in auth-profiles.json

The anthropic:default profile's key field is what OpenClaw actually sends. Change it there.

python
# Example: swap to subscription key as primary, paid key as backup
import json
with open('~/.openclaw/agents/main/agent/auth-profiles.json') as f:
data = json.load(f)
# Set subscription key as default
data['profiles']['anthropic:default'] = {
"type": "api_key",
"provider": "anthropic",
"key": "<subscription-key>"
}
# Add paid key as backup
data['profiles']['anthropic:backup'] = {
"type": "api_key",
"provider": "anthropic",
"key": "<paid-api-key>"
}

Then update openclaw.json auth order:

json
"anthropic": ["anthropic:default", "anthropic:backup"]

One gateway restart. Done.

What DOESN'T work (or is unnecessary)
  • ❌ ~/.openclaw/.env with ANTHROPIC_API_KEY — auth-profiles.json takes priority
  • ❌ OPENCLAW_LIVE_ANTHROPIC_KEYS env var — same, auth-profiles wins
  • ❌ Adding vars to launchd plist — auth-profiles.json is checked first
  • ❌ Searching macOS Keychain — OpenClaw doesn't store Anthropic keys there
  • ❌ Restarting the proxy with debug logging mid-tool-call — kills your own connection
How key resolution actually works
  1. OpenClaw reads auth-profiles.json → finds anthropic:default → gets key field
  2. Passes key to pi-ai as options.apiKey
  3. pi-ai sends it via x-api-key header to the proxy (baseUrl from models.generated.js)
  4. Proxy adds Claude Code envelope → forwards to Anthropic with the same key

Trace the path BEFORE touching anything. Don't gunshoot env vars hoping one sticks.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
111
Repository forks
3
7-day trend score
24

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/john-rood/claude-proxy/anthropic-sub-proxy/badge)](https://verified-skill.com/skills/john-rood/claude-proxy/anthropic-sub-proxy)

Something wrong with this skill or its page? Tell us.