openclaw - 💡(How to fix) Fix Bug: agents.list[].systemPromptOverride is ignored after gateway restart + sessions.json wipe [1 comments, 2 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#72150Fetched 2026-04-27 05:34:09
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Author
Timeline (top)
closed ×1commented ×1

agents.list[].systemPromptOverride (per-agent gateway-level system prompt injection, documented in OpenClaw 2026.4.21) is not re-applied when:

  1. The gateway is restarted (e.g. via openclaw gateway restart or any process recycle).
  2. ~/.openclaw/sessions.json (or its container-side equivalent at /data/.openclaw/sessions.json) is rotated, wiped, or starts empty.

In that scenario, the next user input from a registered inbound (Telegram, webchat) spawns a fresh session in the CLI runner, and the model behaves as if systemPromptOverride were null — even though openclaw config get 'agents.list.0.systemPromptOverride' returns the configured string.

The override appears to be applied only at session creation time, conditionally, and the path that creates a fresh session for a known inbound (registered chat) does not re-read it from agents.list[].systemPromptOverride.

This is a high-impact bug for operators who use systemPromptOverride as a defense-in-depth layer for routing / delegation rules that must survive restarts.


Root Cause

Hypothesis on root cause

Fix Action

Fix / Workaround

Local mitigation we ship today

  1. Duplicates the rule in two places (config drift risk — they fall out of sync).
  2. Inflates per-turn token usage (the override is now in every prompt, not just system).
  3. Is plugin-conditional — if active-memory is disabled, mitigation evaporates.

Code Example

{
     "agents": {
       "list": [
         {
           "id": "main",
           "systemPromptOverride": "RUTA POR DOMINIO (INQUEBRANTABLE):\n- GitHub → cks-dev (delega)\n- Vercel → cks-dev (delega)\n- Supabase → cks-lab (delega)\n- Sergio API → cks-bridge (delega)\n- Ingesta/research → cks-scout (delega)\n\nNUNCA invocar wrappers cks-*.sh desde main. NUNCA usar curl directo a APIs externas. SIEMPRE delegar al leaf de dominio. Si dudas, pregunta antes de actuar."
         }
       ]
     }
   }

---

docker exec openclaw-ald8-openclaw-1 openclaw config get 'agents.list.0.systemPromptOverride'
   # → returns the full string above

---

[UserTelegram] Mergea PR #247
   [Maestro] Delegando a cks-dev para mergear PR #247...
   [cks-dev] PR #247 mergeado ✅

---

bash scripts/gateway-restart-safe.sh
   # PID 4821449085, downtime ~9s

---

docker exec openclaw-ald8-openclaw-1 mv /data/.openclaw/sessions.json /data/.openclaw/sessions.json.bak

---

[UserTelegram] Mergea PR #247

---

[Maestro] Voy a revisar el repo...
   [Maestro requests approval] sh -c 'env | grep -i github'
   [Maestro requests approval] sh -c 'find /data -type f -name "*github*"'
   [Maestro requests approval] sh -c 'pwd && ls'

---

docker exec openclaw-ald8-openclaw-1 mv /data/.openclaw/sessions.json.bak /data/.openclaw/sessions.json
   bash scripts/gateway-restart-safe.sh

---

{
  "plugins": {
    "entries": {
      "active-memory": {
        "config": {
          "promptAppend": "RUTA POR DOMINIO (INQUEBRANTABLE): GitHub→cks-dev / Vercel→cks-dev / Supabase→cks-lab / Sergio API→cks-bridge / Ingesta→cks-scout. NUNCA invocar cks-*.sh desde main. NUNCA curl directo a APIs externas. Tras 1 deny, NO generar variantes — preguntar al operador."
        }
      }
    }
  }
}

---

# Confirm config is set
docker exec openclaw-ald8-openclaw-1 openclaw config get 'agents.list.0.systemPromptOverride'

# Confirm sessions.json state
docker exec openclaw-ald8-openclaw-1 cat /data/.openclaw/sessions.json | jq 'keys'

# After restart + sessions wipe, send Telegram input and inspect:
# 1. event log for delegation events (should appear, doesn't on bug)
docker exec openclaw-ald8-openclaw-1 tail -f /var/log/openclaw-events.jsonl | jq 'select(.kind == "agent_handoff")'

# 2. model invocation log for system prompt content (token count proxy)
docker logs openclaw-ald8-openclaw-1 --tail 200 | grep -i 'system_prompt_chars'
RAW_BUFFERClick to expand / collapse

Bug: agents.list[].systemPromptOverride is ignored after gateway restart + sessions.json wipe (CLI runner spawns fresh session without override)

Summary

agents.list[].systemPromptOverride (per-agent gateway-level system prompt injection, documented in OpenClaw 2026.4.21) is not re-applied when:

  1. The gateway is restarted (e.g. via openclaw gateway restart or any process recycle).
  2. ~/.openclaw/sessions.json (or its container-side equivalent at /data/.openclaw/sessions.json) is rotated, wiped, or starts empty.

In that scenario, the next user input from a registered inbound (Telegram, webchat) spawns a fresh session in the CLI runner, and the model behaves as if systemPromptOverride were null — even though openclaw config get 'agents.list.0.systemPromptOverride' returns the configured string.

The override appears to be applied only at session creation time, conditionally, and the path that creates a fresh session for a known inbound (registered chat) does not re-read it from agents.list[].systemPromptOverride.

This is a high-impact bug for operators who use systemPromptOverride as a defense-in-depth layer for routing / delegation rules that must survive restarts.


Steps to reproduce

Setup

ItemValue
OpenClaw version2026.4.21 (commit f788c88)
DeploymentHostinger official Docker app, container openclaw-ald8-openclaw-1
Sandboxembedded (sandbox.backend unset)
Maestro modelopenai-codex/gpt-5.5 via ChatGPT Plus OAuth
InboundTelegram (allowlist DM, single registered chat)

Steps

  1. Configure agents.list[main].systemPromptOverride to a non-trivial routing rule:

    {
      "agents": {
        "list": [
          {
            "id": "main",
            "systemPromptOverride": "RUTA POR DOMINIO (INQUEBRANTABLE):\n- GitHub → cks-dev (delega)\n- Vercel → cks-dev (delega)\n- Supabase → cks-lab (delega)\n- Sergio API → cks-bridge (delega)\n- Ingesta/research → cks-scout (delega)\n\nNUNCA invocar wrappers cks-*.sh desde main. NUNCA usar curl directo a APIs externas. SIEMPRE delegar al leaf de dominio. Si dudas, pregunta antes de actuar."
          }
        ]
      }
    }
  2. Verify config is persisted:

    docker exec openclaw-ald8-openclaw-1 openclaw config get 'agents.list.0.systemPromptOverride'
    # → returns the full string above
  3. From Telegram, send an input that exercises the routing rule. Confirm the Maestro delegates correctly to the leaf:

    [User → Telegram] Mergea PR #247
    [Maestro] Delegando a cks-dev para mergear PR #247...
    [cks-dev] PR #247 mergeado ✅

    This is the expected baseline. Override is being applied.

  4. Restart the gateway (any reason — config push, image upgrade, OOM kill):

    bash scripts/gateway-restart-safe.sh
    # PID 48214 → 49085, downtime ~9s
  5. Move/rename sessions.json (simulates a fresh deployment, a sessions corruption recovery, or a daily cron that rotates it):

    docker exec openclaw-ald8-openclaw-1 mv /data/.openclaw/sessions.json /data/.openclaw/sessions.json.bak
  6. From Telegram, send the same input:

    [User → Telegram] Mergea PR #247
  7. Observe actual behaviour:

    [Maestro] Voy a revisar el repo...
    [Maestro requests approval] sh -c 'env | grep -i github'
    [Maestro requests approval] sh -c 'find /data -type f -name "*github*"'
    [Maestro requests approval] sh -c 'pwd && ls'

    Maestro performs raw discovery as if no routing rule exists. CWD is main (not cks-dev). No delegation event in event log. The configured systemPromptOverride string is NOT in the model's context — verifiable by comparing token counts of the model invocation logs (pre-restart vs post-restart-with-fresh-session: ~600 tokens shorter).

  8. Restore sessions.json and restart again:

    docker exec openclaw-ald8-openclaw-1 mv /data/.openclaw/sessions.json.bak /data/.openclaw/sessions.json
    bash scripts/gateway-restart-safe.sh

    First Telegram input after restart with the existing session: override is applied again, delegation works.

So the override is loaded at gateway boot and applied to existing sessions, but NOT injected into freshly created sessions when an inbound triggers session creation.


Expected behaviour

systemPromptOverride is read from config at session creation time, regardless of whether the session is new or restored from sessions.json. The model receives the override string in its system prompt on the very first turn of a fresh session.


Actual behaviour

When the CLI runner spawns a fresh session for a registered inbound (Telegram chat already in inbounds.allowlist), the system prompt is constructed without consulting agents.list[].systemPromptOverride. The model behaves as if the override were null.

The configured value is persisted on disk (openclaw.json shows it correctly). It's just not read on this code path.


Hypothesis on root cause

Without source access we can only reason from symptoms. Best-effort hypothesis:

  • There appear to be at least two paths that construct the model's system prompt:

    1. Session-restore path — when sessions.json has an entry for the inbound, the persisted system prompt is loaded (it includes the override from when the session was first created).
    2. Session-create path (CLI runner spawn) — when no entry exists, a new session is constructed. This path appears to read SOUL.md + plugin-injected prompts but skip agents.list[].systemPromptOverride.
  • This would also explain why openclaw config set 'agents.list.0.systemPromptOverride' followed by openclaw gateway restart without wiping sessions.json works fine — the existing sessions retain the override they were created with.

  • It may be related conceptually to PR #68686 ("preserve CLI session on auth-profile/epoch changes") — that PR addresses session preservation across auth changes; this bug suggests the inverse problem is also unhandled (override not preserved across session creation).

If maintainers can confirm the code path, a one-liner fix in the session-create path (read agents.list[].systemPromptOverride and prepend to system prompt) should close the bug.


Operator impact

systemPromptOverride is the only gateway-level mechanism to inject routing / delegation / safety rules that survive SOUL.md edits and plugin disablement. It's documented as the canonical place for "non-negotiable" agent rules.

If it doesn't survive a restart + sessions wipe, then:

  • Any operator who uses sessions.json rotation (ours rotates daily as part of backup) loses the override every 24h.
  • Any cold deploy (new container image, container recreate) starts agents without the override.
  • Defense-in-depth claims for the override are false: the override is only as durable as sessions.json.

Combined with #69570 (approval cascade) and #69386 (post-deny retry), this means a Maestro coming up after a fresh deploy is measurably less safe than the same Maestro that has been warm for a day.


Local mitigation we ship today

We replicate the override into plugins.entries.active-memory.config.promptAppend:

{
  "plugins": {
    "entries": {
      "active-memory": {
        "config": {
          "promptAppend": "RUTA POR DOMINIO (INQUEBRANTABLE): GitHub→cks-dev / Vercel→cks-dev / Supabase→cks-lab / Sergio API→cks-bridge / Ingesta→cks-scout. NUNCA invocar cks-*.sh desde main. NUNCA curl directo a APIs externas. Tras 1 deny, NO generar variantes — preguntar al operador."
        }
      }
    }
  }
}

The active-memory plugin re-injects this string at every turn, so even a fresh session gets the rule on turn 1. This works as a stopgap but:

  1. Duplicates the rule in two places (config drift risk — they fall out of sync).
  2. Inflates per-turn token usage (the override is now in every prompt, not just system).
  3. Is plugin-conditional — if active-memory is disabled, mitigation evaporates.

A real fix on systemPromptOverride would let us delete the promptAppend duplicate and rely on a single source of truth.


Verification commands operators can run

# Confirm config is set
docker exec openclaw-ald8-openclaw-1 openclaw config get 'agents.list.0.systemPromptOverride'

# Confirm sessions.json state
docker exec openclaw-ald8-openclaw-1 cat /data/.openclaw/sessions.json | jq 'keys'

# After restart + sessions wipe, send Telegram input and inspect:
# 1. event log for delegation events (should appear, doesn't on bug)
docker exec openclaw-ald8-openclaw-1 tail -f /var/log/openclaw-events.jsonl | jq 'select(.kind == "agent_handoff")'

# 2. model invocation log for system prompt content (token count proxy)
docker logs openclaw-ald8-openclaw-1 --tail 200 | grep -i 'system_prompt_chars'

Cross-references

  • PR #68686 (open) — preserves CLI session on auth-profile/epoch changes; conceptually adjacent (session lifecycle vs override application).
  • Issue #69570 — approval cascade; same Maestro-loses-context family of problems.
  • Issue #69386 — agent ignores stop commands during exec denial; same family.
  • Our own filed feature request — agents.list[].tools.deny_wrappers — would reduce surface but doesn't fix this bug; the override is the primary defense, deny_wrappers is the backstop.

Repro setup summary

ItemValue
OpenClaw version2026.4.21 (commit f788c88)
Containeropenclaw-ald8-openclaw-1 (Hostinger official Docker app)
Maestro modelopenai-codex/gpt-5.5 (OAuth Codex via ChatGPT Plus, account [email protected])
Maestro reasoningDefault"off"
Maestro thinkingDefault"low"
Maestro tools.execsecurity: "allowlist", ask: "on-miss"
Sandboxembedded (no per-agent docker)
InboundsTelegram (single allowlist DM) + webchat
Other agents4 leafs running nvidia/nemotron-3-super-120b-a12b

Happy to provide:

  • Full openclaw.json (sanitized — secrets redacted) on request
  • Container docker compose config
  • sessions.json before/after wipe diff
  • Telegram chat transcript with both successful (warm session) and failing (cold session) repros side-by-side

Cross-verification (pending)

A second verification on [email protected] is being prepared by another community member who reproduced the same behavior independently. Their snippet showing the absence of persistence code in runtime-state-CrEOE13h.js will be added as a comment when ready, providing two-version coverage (2026.4.21 + 2026.4.22).


Filed by: a self-hosted OpenClaw operator running 5 agents in production for ~3 months. Discovered while debugging systemPromptOverride defense-in-depth coverage as part of a defensive hardening package (ADR-018 in our internal docs).

extent analysis

TL;DR

The most likely fix for the issue is to modify the session creation code path to read and apply the systemPromptOverride configuration.

Guidance

  • Verify that the systemPromptOverride configuration is correctly set and persisted in the openclaw.json file.
  • Check the session creation code path to ensure it reads and applies the systemPromptOverride configuration.
  • Consider adding a temporary workaround by replicating the override into plugins.entries.active-memory.config.promptAppend, but be aware of the limitations and potential issues with this approach.
  • Test the fix by restarting the gateway, wiping the sessions.json file, and sending a Telegram input to verify that the systemPromptOverride is applied correctly.

Example

No code snippet is provided as the issue does not include the relevant code, but the fix would involve modifying the session creation code to read and apply the systemPromptOverride configuration.

Notes

The issue appears to be related to the session creation code path not reading the systemPromptOverride configuration, and a fix would require modifying this code path. The temporary workaround using plugins.entries.active-memory.config.promptAppend may have limitations and potential issues.

Recommendation

Apply a workaround by modifying the session creation code path to read and apply the systemPromptOverride configuration, as this is the most direct and effective solution to the issue.

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 - 💡(How to fix) Fix Bug: agents.list[].systemPromptOverride is ignored after gateway restart + sessions.json wipe [1 comments, 2 participants]