openclaw - ✅(Solved) Fix heartbeat.every values >596h cause Node.js integer overflow, firing heartbeat every 1ms and breaking channel message processing [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#61546Fetched 2026-04-08 02:57:31
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
referenced ×2cross-referenced ×1

Setting heartbeat.every to a very large value (e.g. 99999h) to effectively disable the heartbeat causes a silent Node.js integer overflow. Instead of firing infrequently, the heartbeat fires on every event loop tick — flooding the gateway with TimeoutOverflowWarning errors and breaking inbound message processing.

Error Message

  • Reject or warn on values that would exceed the 32-bit setTimeout limit
  • Surface a clear error message pointing users to "0m" as the correct way to disable the heartbeat entirely

Root Cause

Node.js uses a 32-bit signed integer internally for setTimeout delays. 99999h converted to milliseconds (99999 × 3,600,000 = 359,996,400,000ms) overflows the 32-bit signed integer limit (~2,147,483,647ms, i.e. ~596h). Node.js silently clamps the overflow to 1ms, causing the heartbeat to fire continuously.

Fix Action

Workaround

To disable the heartbeat entirely, use "0m" rather than a large number:

{ "agents": { "defaults": { "heartbeat": { "every": "0m" } } } }

Maximum safe value for a very infrequent heartbeat: approximately 596h.

PR fix notes

PR #61638: fix(config): reject heartbeat.every values that overflow Node.js setTimeout

Description (problem / solution / changelog)

Summary

  • Problem: Setting heartbeat.every to values > ~596h (e.g., 99999h) causes Node.js setTimeout to silently overflow. The 32-bit signed integer limit (~2,147,483,647ms) clamps the delay to 1ms, firing heartbeats every millisecond and breaking channel message processing.
  • Why it matters: Users who set large values to effectively disable heartbeats get the opposite behavior — rapid-fire heartbeats that exhaust resources and break message delivery.
  • What changed: Added overflow validation in HeartbeatSchema.superRefine() to reject values exceeding the setTimeout limit at config load time with a clear error message.
  • What did NOT change (scope boundary): parseDurationMs(), heartbeat-runner.ts, heartbeat-summary.ts — only the Zod schema validation layer is touched.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway / orchestration

Linked Issue/PR

  • Closes #61546
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: HeartbeatSchema.superRefine() validated that the duration string parses correctly but did not check whether the parsed millisecond value exceeds Node.js's 32-bit setTimeout limit.
  • Missing detection / guardrail: No range validation on parsed duration values in the config schema.
  • Prior context: The parseDurationMs() parser checks Number.isFinite() but not the setTimeout ceiling.
  • Why this regressed now: Always present — any user setting heartbeat.every to a very large value would hit this.
  • If unknown, what was ruled out: N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: src/config/zod-schema.heartbeat-overflow.test.ts
  • Scenario the test should lock in:
    1. "99999h" is rejected with error mentioning setTimeout limit
    2. "596h" is accepted (within limit)
    3. "30m" is accepted (typical value)
    4. "abc" still rejected with existing invalid duration message
  • Why this is the smallest reliable guardrail: Validates at the config schema layer, the earliest point where the value is checked.
  • Existing test that already covers this (if any): None
  • If no new test is added, why not: N/A — 4 new tests added

User-visible / Behavior Changes

  • Config validation now rejects heartbeat.every values > ~596h with: heartbeat.every exceeds the Node.js setTimeout limit (~596h). Use "0m" to disable or choose a shorter interval.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Any
  • Runtime/container: Node 22+

Steps

  1. Set agents.defaults.heartbeat.every to "99999h" in config
  2. Run openclaw gateway

Expected

  • Config validation rejects the value with a clear error message

Actual (before fix)

  • Config loads successfully, setTimeout receives an overflowed value, heartbeat fires every ~1ms

Evidence

  • Failing test/log before + passing after
  • 4 new tests in zod-schema.heartbeat-overflow.test.ts all pass

Human Verification (required)

  • Verified scenarios: All 4 tests pass; pnpm check lint passes (pre-existing TS error in unrelated file)
  • Edge cases checked: Boundary value (596h accepted), typical value (30m accepted), invalid syntax preserved
  • What you did not verify: Live gateway test with the overflow value

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes (only rejects previously-broken values)
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: Users with existing heartbeat.every > 596h configs will get a validation error on startup.
    • Mitigation: The error message explicitly suggests "0m" to disable or a shorter interval. These configs were already silently broken (firing every 1ms), so catching them is an improvement.

AI-assisted: Yes (Claude). Fully tested. I understand what the code does.

Changed files

  • src/config/zod-schema.agent-runtime.ts (modified, +14/-1)
  • src/config/zod-schema.heartbeat-overflow.test.ts (added, +52/-0)

Code Example

{ "agents": { "defaults": { "heartbeat": { "every": "99999h" } } } }

---

{ "agents": { "defaults": { "heartbeat": { "every": "0m" } } } }
RAW_BUFFERClick to expand / collapse

Description

Setting heartbeat.every to a very large value (e.g. 99999h) to effectively disable the heartbeat causes a silent Node.js integer overflow. Instead of firing infrequently, the heartbeat fires on every event loop tick — flooding the gateway with TimeoutOverflowWarning errors and breaking inbound message processing.

Steps to Reproduce

  1. Set heartbeat.every to a large value intended to disable it, e.g.:
    { "agents": { "defaults": { "heartbeat": { "every": "99999h" } } } }
  2. Restart the gateway
  3. Send a message via a connected channel (e.g. Discord)

Expected Behaviour

Heartbeat fires very infrequently (or not at all). Inbound channel messages are processed normally.

Actual Behaviour

The gateway floods the event loop with TimeoutOverflowWarning errors. Inbound messages from channels stop being processed — the bot becomes unresponsive to incoming messages while still appearing to run. This made the root cause very hard to diagnose: openclaw status showed Discord as connected and healthy, but no messages were getting through.

Root Cause

Node.js uses a 32-bit signed integer internally for setTimeout delays. 99999h converted to milliseconds (99999 × 3,600,000 = 359,996,400,000ms) overflows the 32-bit signed integer limit (~2,147,483,647ms, i.e. ~596h). Node.js silently clamps the overflow to 1ms, causing the heartbeat to fire continuously.

Workaround

To disable the heartbeat entirely, use "0m" rather than a large number:

{ "agents": { "defaults": { "heartbeat": { "every": "0m" } } } }

Maximum safe value for a very infrequent heartbeat: approximately 596h.

Suggested Fix

Add input validation when parsing heartbeat.every to:

  • Reject or warn on values that would exceed the 32-bit setTimeout limit
  • Surface a clear error message pointing users to "0m" as the correct way to disable the heartbeat entirely

extent analysis

TL;DR

To fix the issue, set heartbeat.every to "0m" instead of a large value to disable the heartbeat entirely, or use a value below the maximum safe limit of approximately 596h.

Guidance

  • To avoid the integer overflow issue, ensure that the heartbeat.every value is set to a number that, when converted to milliseconds, does not exceed the 32-bit signed integer limit.
  • Use the suggested workaround of setting heartbeat.every to "0m" to disable the heartbeat entirely, as this is a safe and intended way to achieve this behavior.
  • When setting a very infrequent heartbeat, use a value below the maximum safe limit of approximately 596h to prevent the overflow issue.
  • Consider implementing input validation for heartbeat.every to reject or warn on values that would exceed the 32-bit setTimeout limit, providing a clear error message pointing users to the correct way to disable the heartbeat.

Example

{ "agents": { "defaults": { "heartbeat": { "every": "0m" } } } }

This sets the heartbeat.every value to "0m", effectively disabling the heartbeat entirely.

Notes

The issue is specific to Node.js and its use of 32-bit signed integers for setTimeout delays. The suggested fix and workaround are intended to mitigate this specific issue.

Recommendation

Apply the workaround by setting heartbeat.every to "0m" to disable the heartbeat entirely, as this is a safe and intended way to achieve this behavior, and it avoids the integer overflow 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