Skip to content

Per-channel timeout configuration (telegram/discord/webchat/cron) #74249

Description

@fertilejim

Problem

Today agents.defaults.timeoutSeconds is a single global knob that applies to all interactive channels (Telegram, Webchat, Discord) plus is the default for cron jobs without explicit per-job override.

This forces a single trade-off across very different latency expectations:

Channel User expectation Realistic upper-bound
Telegram (interactive chat) reply within seconds, fail-fast on hang 60–120s
Discord (interactive) similar to Telegram 60–120s
Webchat (UI) can wait for compaction / longer chains 180–300s
Cron (background) varies by job; some up to 30 min 60–1800s, per job

Because all interactive channels share one timeout:

  • If you lower it (e.g. 90s) to fail-fast on hung profiles → legitimate slow runs (Anthropic Opus compaction on large contexts) get killed
  • If you raise it (e.g. 360s) so compaction survives → a single hung OAuth profile (silent server-side throttle, no 429) blocks Telegram for 6 minutes

In production this manifests as continuous tweaking of the global timeout each time a different bug surfaces. My team's memory file shows: 180s → 240s → 360s → 600s → back down → up — over months, with no architectural progress, just whack-a-mole.

Reproduction (silent profile-hang scenario)

  1. Configure 3 OAuth profiles for openai-codex (e.g. ChatGPT Plus accounts)
  2. Hit one profile's daily quota → it returns Try again in ~N min
  3. After the cooldown elapses, OpenAI sometimes leaves that profile in a soft-throttle state where requests hang without responding (no 429, no error, just dead connection)
  4. Send a Telegram message that routes to the throttled profile via advanceAuthProfile
  5. Wait 6 minutes (the global timeoutSeconds) before failover happens
  6. The user-facing channel (Telegram) appears completely unresponsive

Proposed solution

Allow per-channel timeout overrides in agents.defaults and agents.list[i]:

```jsonc
{
"agents": {
"defaults": {
"timeoutSeconds": 360,
"channelTimeoutSeconds": {
"telegram": 90,
"discord": 90,
"webchat": 240
}
}
}
}
```

Resolution order at request time (highest priority first):

  1. Cron-job specific payload.timeoutSeconds
  2. Per-agent channelTimeoutSeconds[channel]
  3. Global channelTimeoutSeconds[channel]
  4. Per-agent timeoutSeconds
  5. Global timeoutSeconds
  6. DEFAULT_AGENT_TIMEOUT_SECONDS (600s in code)

Why this is more than just config sugar

The current architecture makes Telegram the worst-served lane: users expect <30s replies, but the global timeout has to accommodate Opus-compaction or 200k-context inference that legitimately needs 3–5 minutes. There's no way today to encode "Telegram should fail fast even if other lanes need longer".

Per-channel timeouts also enable a much faster failover loop on hung profiles. With Telegram at 90s, a silent-hang profile is rotated within 1.5 min instead of 6 min.

Related

Both share the same underlying pain: the gateway has no granularity for "Telegram needs to fail fast".

Workaround used today (suboptimal)

  • Manually clear authProfileOverride per session when a profile hangs
  • Manually demote the hanging profile via openclaw models auth order set
  • Accept that the global timeout will be "wrong" for at least one lane

This is reactive whack-a-mole and doesn't scale.

Acceptance criteria

  • channelTimeoutSeconds parses from config, schema-validated
  • Runtime resolves the right timeout per request based on channel type
  • Existing timeoutSeconds semantics unchanged when channelTimeoutSeconds not set (backward compatible)
  • Reasonable defaults shipped (e.g. telegram=120, discord=120, webchat=300, fallback to global)
  • Documented in agent config reference

OpenClaw version: 2026.4.24

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions