-
Notifications
You must be signed in to change notification settings - Fork 574
Description
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
- Plugin version: memory-lancedb-pro 1.1.0-beta.10
- OpenClaw version: v2026.3.24
- Platform: Debian 13 (headless), Node v24.14.0
- Transport: Telegram
- Config:
OPENCLAW_EXTENSION_API_PATHset per Compatibility issue: legacy openclaw/extension-api runtime loading on newer OpenClaw builds #357 workaround
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:newevents 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
- Install memory-lancedb-pro, set
OPENCLAW_EXTENSION_API_PATHper Compatibility issue: legacy openclaw/extension-api runtime loading on newer OpenClaw builds #357 - Enable reflection (
selfImprovement.enabled: true,memoryReflection.enabled: true) - Send a message to the bot via Telegram to create/continue a session
- Send
/newor let the session naturally transition - Observe: first reflection generates normally, then it self-triggers 3 more times
- 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:
- Writes a reflection
.mdfile tomemory/reflections/YYYY-MM-DD/ - Writes an entry to
memory/YYYY-MM-DD.md(daily log) - Stores vectors in LanceDB
- Possibly triggers a side-effect (file watcher, session update, or embedded runner side-effect) that causes OpenClaw to re-emit
command:newfor 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