Skip to content

Bug: Reflection self-triggers in a loop — same session generates 4 near-identical reflections #382

@superfat1988

Description

@superfat1988

Bug: Reflection self-triggers in a loop — same session generates 4 near-identical reflections

Related to #357 (OPENCLAW_EXTENSION_API_PATH workaround).

Summary

After resolving the OPENCLAW_EXTENSION_API_PATH issue from #357, the memory-reflection feature works but enters a self-triggering loop: every time a reflection completes writing, it immediately re-triggers the command:new hook for the same session, producing 4 near-identical reflection documents within ~2 minutes.

Environment

Evidence

1. Gateway log timeline (from /tmp/openclaw/openclaw-2026-03-26.log)

All 4 reflections share the same sessionId=dea76b16-f754-4b32-937b-78516085730d and sessionKey=agent:main:telegram:default:direct:667042947:

22:06:31.822  hook start
22:07:14.419  wrote 140631868-*.md  (took ~43s)
22:07:14.420  hook start  ← re-triggered 1ms after write
22:07:49.675  wrote 140714424-*.md  (took ~35s)
22:07:49.677  hook start  ← re-triggered 2ms after write
22:08:29.707  wrote 140749680-*.md  (took ~40s)
22:08:29.709  hook start  ← re-triggered 2ms after write
22:09:04.312  wrote 140829712-*.md  (took ~35s)

Every single reflection completion is followed by an immediate (1-2ms) hook start for the same session.

2. Output files

memory/reflections/2026-03-26/
├── 140631868-main-dea76b16-f754-4b32-937b-78516085.md  (6863 bytes)
├── 140714424-main-dea76b16-f754-4b32-937b-78516085.md  (6892 bytes)
├── 140749680-main-dea76b16-f754-4b32-937b-78516085.md  (6791 bytes)
└── 140829712-main-dea76b16-f754-4b32-937b-78516085.md  (6790 bytes)

All 4 files have different md5 hashes (LLM re-generation produces slight wording variations) but are substantively identical: same session context, same conclusions, same decisions, same lessons.

3. No external trigger

  • No user messages triggered the re-fires (no Telegram messages, no webchat, no other command:new events in the 22:06–22:09 window besides the reflection hook's own)
  • No gateway restart occurred during this window (restart happened later at 22:13)
  • Heartbeat/cron timer arming every 60s does not trigger command:new

Reproduction

  1. Install memory-lancedb-pro, set OPENCLAW_EXTENSION_API_PATH per Compatibility issue: legacy openclaw/extension-api runtime loading on newer OpenClaw builds #357
  2. Enable reflection (selfImprovement.enabled: true, memoryReflection.enabled: true)
  3. Send a message to the bot via Telegram to create/continue a session
  4. Send /new or let the session naturally transition
  5. Observe: first reflection generates normally, then it self-triggers 3 more times
  6. Each cycle takes ~35-45s (LLM generation time), creating 4 near-identical files

Root cause hypothesis

The runMemoryReflection hook registered on command:new (line ~3365 in index.ts) has no re-entry guard. After generating a reflection, the hook:

  1. Writes a reflection .md file to memory/reflections/YYYY-MM-DD/
  2. Writes an entry to memory/YYYY-MM-DD.md (daily log)
  3. Stores vectors in LanceDB
  4. Possibly triggers a side-effect (file watcher, session update, or embedded runner side-effect) that causes OpenClaw to re-emit command:new for the same session

The 1-2ms gap between wrote and hook start strongly suggests the re-trigger is synchronous with the hook's own write operations, not an external event.

Suggested fix

Add a session-level re-entry guard to runMemoryReflection:

// At module level or in reflection state
const reflectionInProgress = new Set<string>();

const runMemoryReflection = async (event: any) => {
  const sessionKey = typeof event.sessionKey === "string" ? event.sessionKey : "";
  
  // Re-entry guard
  if (sessionKey && reflectionInProgress.has(sessionKey)) {
    api.logger.info(`memory-reflection: re-entry detected for ${sessionKey}, skipping`);
    return;
  }
  
  if (sessionKey) reflectionInProgress.add(sessionKey);
  try {
    // ... existing logic ...
  } finally {
    if (sessionKey) reflectionInProgress.delete(sessionKey);
    // ... existing cleanup ...
  }
};

Alternatively, add a cooldown mechanism that prevents reflection from running again for the same session within a configurable window (e.g., 5 minutes).

Impact

  • Resource waste: 4x the LLM API calls, 4x the LanceDB writes, 4x the disk I/O for every session transition
  • Cost: Each reflection run consumes tokens. 4 redundant runs = 3x unnecessary cost per session
  • Data quality: 3 near-duplicate reflection entries per session pollute the memory store and weekly review aggregation
  • User experience: Reflection output (which may be surfaced to users) becomes noisy with duplicate content

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions