Skip to content

LINE channel crash loop: monitorLineProvider resolves immediately instead of blocking on abortSignal #26978

@cattia-claw

Description

@cattia-claw

Bug Summary

The LINE channel enters an infinite crash loop immediately after starting. The monitorLineProvider() async function resolves instantly after registering the webhook HTTP handler, causing the channel manager to interpret this as "channel exited" and trigger restart logic.

Reproduction

  1. Configure a LINE channel in openclaw.json with valid credentials
  2. Enable the channel (enabled: true)
  3. Observe gateway logs:
[line] [default] starting LINE provider (Cattia)
[line] [default] auto-restart attempt 1/10 in 5s
[line] [default] starting LINE provider (Cattia)
[line] [default] auto-restart attempt 2/10 in 10s
...
[line] [default] giving up after 10 restart attempts
[health-monitor] [line:default] health-monitor: restarting (reason: gave-up)

The cycle repeats indefinitely: 10 exponential-backoff restart attempts → give up → health monitor restarts → repeat.

Root Cause

In extensions/line/src/channel.ts line 654, startAccount returns the result of monitorLineProvider():

// extensions/line/src/channel.ts:654
return getLineRuntime().channel.line.monitorLineProvider({...});

monitorLineProvider() (in src/line/monitor.ts) registers the webhook HTTP handler and immediately returns { account, handleWebhook, stop }. It does NOT block on the abortSignal.

The channel manager in gateway-cli (line ~2330) expects startAccount to return a long-running promise that only resolves when the channel is stopped. When it resolves immediately, .finally() sets running: false and .then() triggers auto-restart.

Comparison with Telegram

Telegram's monitorTelegramProvider() correctly blocks on the abort signal in webhook mode (compiled bundle line ~61170):

// After registering webhook handler:
if (abortSignal && !abortSignal.aborted) await new Promise((resolve) => {
    const onAbort = () => {
        abortSignal.removeEventListener("abort", onAbort);
        resolve();
    };
    abortSignal.addEventListener("abort", onAbort, { once: true });
});

LINE's monitorLineProvider() is missing this pattern entirely.

Fix

Add the abort-signal blocking pattern to monitorLineProvider() in src/line/monitor.ts, after the abortSignal?.addEventListener("abort", stopHandler) line and before the return statement:

abortSignal?.addEventListener("abort", stopHandler);

// Add this block to keep the promise alive until abort
if (abortSignal && !abortSignal.aborted) {
    await new Promise<void>((resolve) => {
        const onAbort = () => {
            abortSignal.removeEventListener("abort", onAbort);
            resolve();
        };
        abortSignal.addEventListener("abort", onAbort, { once: true });
    });
}

return { account: bot.account, handleWebhook: bot.handleWebhook, stop: ... };

Impact

  • LINE channel is completely unusable without this fix
  • The crash loop consumes gateway resources (timers, log spam)
  • Health monitor's 3-restarts/hour limit is exhausted by LINE, potentially delaying recovery of other channels

Environment

  • OpenClaw version: 2026.2.24
  • Platform: macOS (darwin arm64)
  • Node.js: 22.22.0

Workaround

Manually patch monitorLineProvider() in the compiled dist bundles:

  • dist/subagent-registry-*.js
  • dist/pi-embedded-*.js
  • dist/reply-*.js

Note: patches are overwritten on npm update.

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions