Dream runs for 1–2 h looping on the same read_file calls; dream.maxIterations is deprecated/ignored so the global 200-iteration cap applies
Bug Description
Scheduled Dream consolidation runs turn into very long agent loops (25–111 minutes each, up to ~200 tool calls), during which the model alternates between re-reading the same two files dozens of times. dream.maxIterations in config has no effect: the source marks it deprecated (config/schema.py: max_iterations: int = Field(default=15, ge=1) # Deprecated: no longer used) and cmd_dream in command/builtin.py calls loop.process_direct(...) without a max_iterations, so Dream inherits the global agents.defaults.maxToolIterations (200).
Over the last ~26 hours of gateway logs:
dream 09-14 10:16 ran ~25 min 202 tool calls
dream 09-14 12:42 ran ~26 min 201 tool calls
dream 09-14 15:10 ran ~36 min 201 tool calls
dream 09-14 17:47 ran ~95 min 31 tool calls
dream 09-15 05:50 ran ~70 min 12 tool calls
dream 09-15 07:50 ran ~111 min 31 tool calls
dream 09-15 09:54 ran ~91 min 168 tool calls (session latency_ms: 5447214)
The runs with ~200 tool calls are clearly hitting the global 200 cap. The session checkpoint for the last run shows runtime_checkpoint.iteration: 164, phase tools_completed, on the same read_file call. The reasoning_content shows the model re-deliberating where to route a single small fact (e.g. "python is not on PATH, use python3") over and over, re-reading the same 25 lines of memory/history.jsonl and the same 60 lines of one SKILL.md each time.
Steps to Reproduce
- Run
nanobot gateway(v0.3.0) with the default Dream config (intervalH: 2,maxIterations: 15,modelOverride: null) and a workspace with a few hundredmemory/history.jsonlentries and ~50 skills. - Use a reasoning model as the default (here
deepseek/deepseek-v4-flash-0731via OpenRouter, temperature 0.1). - Wait for the scheduled
Cron: executing job 'dream' (dream). - Watch the log:
Tool call: read_file(...)for the same two paths repeats every ~30 s for an hour or more; the run only ends when the globalmaxToolIterations(200) is reached or the model eventually stops on its own.
Expected Behavior
- A Dream-specific iteration cap that actually bounds the run (
dream.maxIterations), or the key removed from the default config and the deprecation documented, so users aren't led to believe it does something. - Ideally a guard against repeated identical tool calls (same tool + same args N times in a row) during Dream, since consolidation is a narrow task and shouldn't need 200 iterations.
Relevant Logs
2026-09-15 09:54:17 | INFO | - | Cron: executing job 'dream' (dream)
2026-09-15 09:54:17 | INFO | - | Processing message from cli:user: You are a memory consolidation engine. Your sole task is to analyze conversation...
2026-09-15 09:55:09 | INFO | - | Tool call: exec({"command": "tail -c 20000 /root/.nanobot/workspace/memory/history.jsonl 2>/dev/null | head -c 20000"})
...
2026-09-15 10:53:36 | INFO | - | Tool call: read_file({"force": true, "limit": 25, "offset": 547, "path": "memory/history.jsonl"})
2026-09-15 10:53:48 | INFO | - | Tool call: read_file({"limit": 60, "path": "skills/<skill-name>/SKILL.md"})
2026-09-15 10:53:54 | INFO | - | Tool call: read_file({"force": true, "limit": 25, "offset": 547, "path": "memory/history.jsonl"})
2026-09-15 10:54:09 | INFO | - | Tool call: read_file({"limit": 60, "path": "skills/<skill-name>/SKILL.md"})
2026-09-15 10:54:39 | INFO | - | Tool call: read_file({"force": true, "limit": 25, "offset": 547, "path": "memory/history.jsonl"})
2026-09-15 10:55:15 | INFO | - | Tool call: read_file({"limit": 60, "path": "skills/<skill-name>/SKILL.md"})
... (same pair repeats ~80 more times until 11:25)
Session checkpoint (sessions/dream:20260915-095417):
"runtime_checkpoint": {"phase": "tools_completed", "iteration": 164, "model": "deepseek/deepseek-v4-flash-0731",
"assistant_message": {"role": "assistant", "content": "", "tool_calls": [{"function": {"name": "read_file",
"arguments": "{\"force\": true, \"limit\": 25, \"offset\": 547, \"path\": \"memory/history.jsonl\"}"}}], ...
nanobot Version
0.3.0
Python Version
3.12
Operating System
Linux
Channel / Platform
Discord
LLM Provider
OpenRouter
Configuration (Optional)
agents:
defaults:
model: deepseek/deepseek-v4-flash-0731
provider: openrouter
maxTokens: 8192
contextWindowTokens: 65536
temperature: 0.1
maxToolIterations: 200
reasoningEffort: null
dream:
intervalH: 2
modelOverride: null
maxBatchSize: 20
maxIterations: 15
annotateLineAges: true
Additional Context
- Each Dream burns up to ~200 LLM calls with a ~25k-token prompt, running 1–2 hours out of every 2-hour interval. On a 2-vCPU box it is the top CPU consumer and noticeably competes with live Discord replies.
- Related but different from #5441 (cursor blocked by a tool error): here there is no error, the model just never converges.
- Workarounds I'm considering: raising
intervalH, settingdream.modelOverrideto a faster/non-reasoning model, or disabling Dream. Happy to share the full Dream session jsonl (redacted) if useful.
Source: HKUDS/nanobot