openclaw - ✅(Solved) Fix [Bug] Gateway in-memory auth cache overwrites per-agent API keys on disk [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#55562Fetched 2026-04-08 01:38:00
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
cross-referenced ×1referenced ×1

When per-agent API keys are configured in individual auth-profiles.json files (e.g., separate xAI keys for different agents), the gateway's in-memory auth store cache silently overwrites them back to the main agent's key.

Root Cause

prepareSecretsRuntimeSnapshot() in auth-profiles-DRjqKE3G.js loads all agent auth stores into memory on gateway startup. This snapshot is never invalidated when on-disk files change.

When updateAuthProfileStoreWithLock() runs (e.g., to update usageStats or lastGood after a model call), it calls ensureAuthProfileStore() which returns the stale in-memory snapshot via resolveRuntimeAuthProfileStore(). The stale snapshot contains the main agent's key for all agents. saveAuthProfileStore() then writes this stale data back to disk, overwriting the per-agent keys.

Call chain:

updateAuthProfileStoreWithLock()
  → ensureAuthProfileStore(agentDir)
    → resolveRuntimeAuthProfileStore(agentDir)  // returns stale cached store
  → updater(store)  // updates usageStats
  → saveAuthProfileStore(store, agentDir)  // writes stale key + updated stats to disk

Fix Action

Workaround

Restarting the gateway (systemctl restart openclaw.service) after editing per-agent keys forces a fresh snapshot load from disk. Keys then persist correctly through spawn cycles.

PR fix notes

PR #55670: fix: discord OOM, OpenAI tool adjacency, subagent fallback, auth cache

Description (problem / solution / changelog)

Summary

Fixes four independent high-impact bugs:

  • #55606 — Discord health-monitor triggers excessive stale-socket reconnects on quiet servers, leaking memory until OOM. Removed blanket lastEventAt inflation from debug messages and added a quiet-server guard using lastConnectedAt so connected channels with no real events aren't treated as stale.

  • #55544 — Session reset prompt (/new, /reset) breaks tool_call adjacency for custom OpenAI-compatible providers (HTTP 400). Enabled validateAnthropicTurns for all openai-completions providers so orphaned tool_calls from prior sessions are stripped before sending.

  • #55581 — Subagent loses its original task when model fallback triggers, receiving a generic "Continue where you left off" recovery message instead. Now preserves the original body when isNewSession is true since the transcript is empty and the fallback model needs the actual task.

  • #55562 — Gateway in-memory auth cache overwrites per-agent API keys on disk during usage/cooldown updates. Added runtime cache write-through after locked saves so subsequent reads via ensureAuthProfileStore() return fresh data.

Test plan

  • pnpm vitest run src/gateway/channel-health-policy.test.ts — 16 tests pass (2 new)
  • pnpm vitest run src/agents/transcript-policy.policy.test.ts — 4 tests pass (2 new)
  • pnpm vitest run src/agents/auth-profiles.runtime-snapshot-save.test.ts — 2 tests pass (1 new)
  • pnpm tsc --noEmit — clean

Changed files

  • extensions/discord/src/monitor/provider.lifecycle.reconnect.ts (modified, +4/-1)
  • src/agents/agent-command.ts (modified, +1/-0)
  • src/agents/auth-profiles.runtime-snapshot-save.test.ts (modified, +55/-0)
  • src/agents/auth-profiles/store.ts (modified, +7/-0)
  • src/agents/command/attempt-execution.ts (modified, +7/-1)
  • src/agents/pi-embedded-helpers.validate-turns.test.ts (modified, +46/-0)
  • src/agents/pi-embedded-helpers/turns.ts (modified, +92/-19)
  • src/agents/transcript-policy.policy.test.ts (modified, +18/-0)
  • src/agents/transcript-policy.test.ts (modified, +5/-2)
  • src/agents/transcript-policy.ts (modified, +6/-1)
  • src/gateway/channel-health-policy.test.ts (modified, +42/-0)
  • src/gateway/channel-health-policy.ts (modified, +12/-0)

Code Example

~/.openclaw/agents/agent-1/agent/auth-profiles.json  → key A
   ~/.openclaw/agents/agent-2/agent/auth-profiles.json  → key B

---

updateAuthProfileStoreWithLock()
ensureAuthProfileStore(agentDir)
resolveRuntimeAuthProfileStore(agentDir)  // returns stale cached store
updater(store)  // updates usageStats
saveAuthProfileStore(store, agentDir)  // writes stale key + updated stats to disk
RAW_BUFFERClick to expand / collapse

Description

When per-agent API keys are configured in individual auth-profiles.json files (e.g., separate xAI keys for different agents), the gateway's in-memory auth store cache silently overwrites them back to the main agent's key.

Steps to Reproduce

  1. Configure unique API keys per agent by editing each agent's auth-profiles.json:
    ~/.openclaw/agents/agent-1/agent/auth-profiles.json  → key A
    ~/.openclaw/agents/agent-2/agent/auth-profiles.json  → key B
  2. Wait for agent spawns via cron jobs
  3. Check the files again — agent-2's key has been reverted to key A (the main agent's key)

Root Cause

prepareSecretsRuntimeSnapshot() in auth-profiles-DRjqKE3G.js loads all agent auth stores into memory on gateway startup. This snapshot is never invalidated when on-disk files change.

When updateAuthProfileStoreWithLock() runs (e.g., to update usageStats or lastGood after a model call), it calls ensureAuthProfileStore() which returns the stale in-memory snapshot via resolveRuntimeAuthProfileStore(). The stale snapshot contains the main agent's key for all agents. saveAuthProfileStore() then writes this stale data back to disk, overwriting the per-agent keys.

Call chain:

updateAuthProfileStoreWithLock()
  → ensureAuthProfileStore(agentDir)
    → resolveRuntimeAuthProfileStore(agentDir)  // returns stale cached store
  → updater(store)  // updates usageStats
  → saveAuthProfileStore(store, agentDir)  // writes stale key + updated stats to disk

Workaround

Restarting the gateway (systemctl restart openclaw.service) after editing per-agent keys forces a fresh snapshot load from disk. Keys then persist correctly through spawn cycles.

Expected Behavior

Per-agent API keys in auth-profiles.json should be preserved across agent spawns. The gateway should either:

  1. Check file mtime before writing and re-read if changed (cache invalidation)
  2. Only write back the specific fields it modified (e.g., usageStats, lastGood) instead of the entire store
  3. Watch auth-profiles files for changes and refresh the in-memory snapshot

Environment

  • OpenClaw version: 2026.3.13
  • OS: Ubuntu 24.04 (Hetzner VPS)
  • Node: v22.x
  • Running as systemd service with gateway
  • 5 agents with cron-based spawns (every 2 minutes)

Impact

Makes it impossible to use separate API keys per agent without restarting the gateway after every key change. Rate-limit isolation between agents is defeated since they all end up sharing the main agent's key.

extent analysis

Fix Plan

To resolve the issue of per-agent API keys being overwritten, we need to implement cache invalidation or partial writes. Here are the steps:

  • Modify ensureAuthProfileStore() to check the file modification time (mtime) before returning the cached store.
  • If the file has been modified, reload the store from disk.
  • Alternatively, modify saveAuthProfileStore() to only write back the specific fields that were modified.

Example Code

// Modified ensureAuthProfileStore() function
function ensureAuthProfileStore(agentDir) {
  const storePath = path.join(agentDir, 'auth-profiles.json');
  const cachedStore = getRuntimeAuthProfileStore(agentDir);
  const stats = fs.statSync(storePath);
  if (cachedStore.mtime !== stats.mtime) {
    // Reload store from disk if file has been modified
    const store = loadAuthProfileStore(storePath);
    setRuntimeAuthProfileStore(agentDir, store);
    return store;
  }
  return cachedStore;
}

// Modified saveAuthProfileStore() function
function saveAuthProfileStore(store, agentDir) {
  const storePath = path.join(agentDir, 'auth-profiles.json');
  const currentStore = loadAuthProfileStore(storePath);
  // Only write back the specific fields that were modified
  const updatedStore = { ...currentStore, ...store };
  fs.writeFileSync(storePath, JSON.stringify(updatedStore, null, 2));
}

Verification

To verify that the fix worked, follow these steps:

  • Configure unique API keys per agent by editing each agent's auth-profiles.json file.
  • Wait for agent spawns via cron jobs.
  • Check the files again to ensure that the per-agent keys have not been overwritten.

Extra Tips

  • Consider implementing file watching to refresh the in-memory snapshot when auth-profiles files change.
  • Review the code to ensure that all places where the auth store is accessed and modified are using the updated functions.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug] Gateway in-memory auth cache overwrites per-agent API keys on disk [1 pull requests, 1 participants]