openclaw - ✅(Solved) Fix [Telegram] statusReactions + ackReaction not firing on OpenClaw 2026.4.5 [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#67859Fetched 2026-04-17 08:29:15
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1unsubscribed ×1

Error Message

"error": "💔",

  • Final reaction is 👍 (done) or 💔 (error)
  • #45906 — statusReactions shows error emoji even for success (implies it DOES fire for some users) Is channels.telegram.messages the correct path for this config? Or does it belong elsewhere (e.g., agents.defaults.messages)? We tried agents.defaults.messages but got "Unrecognized key" validation error.

Fix Action

Fixed

PR fix notes

PR #67890: Doctor/config: repair misplaced channels.<id>.messages.* (fixes #67859)

Description (problem / solution / changelog)

Summary

  • Problem: Users who nest reaction config under channels.<id>.messages.* (for example the reporter's `channels.telegram.messages.statusReactions` / `ackReaction` / `ackReactionScope`) observe no ack reaction and no status reactions. The runtime only reads these keys from top-level `messages.*` (plus `channels.<id>.ackReaction` / `.responsePrefix` for the two channel-scoped variants), so the nested keys are silently dropped with no user-visible signal.
  • Why it matters: There is no actionable error today — config validates (the top-level `ChannelsSchema` uses `passthrough()`) and the channel boots, the bot just never reacts. The reporter (and this is not the first time this path confuses users) has no way to self-diagnose the mismatch.
  • What changed: New `openclaw doctor --fix` legacy-config migration `channels.<id>.messages->messages` that detects misplaced `channels.<id>.messages.` and `channels.<id>.accounts.<aid>.messages.` blocks and relocates each known `MessagesConfig` key to its canonical location. Channel-scoped keys (`ackReaction`, `responsePrefix`) stay on the channel/account scalar so the user's per-channel intent is preserved; the other `MessagesConfig` keys (`ackReactionScope`, `removeAckAfterReply`, `statusReactions`, `suppressToolErrors`, `messagePrefix`, `groupChat`, `queue`, `inbound`, `tts`) land at top-level `messages.*`. Covers both channel-level and account-level misplacement; honors existing destination values (logs a removed note instead of clobbering); never touches unknown keys (third-party plugin config is preserved).
  • What did NOT change (scope boundary): No changes to the reaction pipeline itself — `resolveAckReaction`, `shouldAckReactionGate`, and `createStatusReactionController` still read from the same canonical locations they already did. No changes to channel/message schemas, defaults, runtime gating, or SDK surface. No config keys were retired, added, or aliased into the public contract.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

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

Root Cause (if applicable)

  • Root cause: No schema rejects `channels.<id>.messages` (the outer `ChannelsSchema` is `passthrough()` to tolerate third-party channel configs), and no runtime code path reads from that nested location — runtime reads `cfg.messages.ackReactionScope`, `cfg.messages.statusReactions`, `cfg.messages.removeAckAfterReply` (see `extensions/telegram/src/bot.ts` and `extensions/telegram/src/bot-message-context.ts`) and `resolveAckReaction` (see `src/agents/identity.ts`). The result: misplaced keys are silently ignored with no repair signal.
  • Missing detection / guardrail: Doctor had no migration spec for this shape, so `openclaw doctor` did not flag or repair it.
  • Contributing context: The reporter's JSON looks intuitively "Telegram-scoped", but the actual contract keeps `messages` globally at the top level and exposes per-channel `ackReaction` / `responsePrefix` scalars at `channels.<id>.*` (not under a `messages` sub-object).

Regression Test Plan (if applicable)

  • Coverage level that should have caught this: [x] Unit test
  • Target test or file: `src/commands/doctor/shared/legacy-config-migrations.channel-messages.test.ts`
  • Scenario the test should lock in: Config with misplaced `channels.<id>.messages.` (channel-level) and `channels.<id>.accounts.<aid>.messages.` (account-level) is migrated to canonical locations; destination-already-set paths are honored; unknown third-party keys are preserved; non-record top-level `messages` is never clobbered.
  • Why this is the smallest reliable guardrail: Drives the migration through the production aggregator (`LEGACY_CONFIG_MIGRATIONS`) with raw `unknown` input and asserts the exact post-migration shape plus `changes` strings. No mocks, no helper defaults.
  • Existing test that already covers this (if any): None.

User-visible / Behavior Changes

  • `openclaw doctor` now warns when `channels.<id>.messages.` or `channels.<id>.accounts.<aid>.messages.` is present.
  • `openclaw doctor --fix` (and `--repair`) now repairs those configs in place and rewrites `~/.openclaw/openclaw.json`:
    • Channel-scoped keys move to `channels.<id>.<key>` / `channels.<id>.accounts.<aid>.<key>` (`ackReaction`, `responsePrefix`).
    • Global-only keys move to `messages.<key>` (`ackReactionScope`, `removeAckAfterReply`, `statusReactions`, `suppressToolErrors`, `messagePrefix`, `groupChat`, `queue`, `inbound`, `tts`).
  • Runtime behavior of Telegram/other channel reactions is unchanged for correctly-configured users; the fix only makes a previously silent misconfiguration self-healing.

Diagram (if applicable)

```text Before (silently ignored by runtime): channels.telegram.messages.statusReactions.enabled: true channels.telegram.messages.ackReactionScope: "all" channels.telegram.messages.ackReaction: "👀"

After (doctor --fix): channels.telegram.ackReaction: "👀" # channel-scoped scalar messages.ackReactionScope: "all" # global (runtime-read path) messages.statusReactions: { enabled: true } # global (runtime-read path) ```

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
  • Notes: The underlying reaction gating is enforced at runtime by `shouldAckReactionGate` / `resolveAckReaction` / `createStatusReactionController` (not by prompt text), and those functions are unchanged. Doctor only relocates config bytes to the canonical locations those functions already read. The migration never clobbers data it does not own: unknown nested keys are preserved, non-record top-level `messages` is refused, and existing destination values are honored (the misplaced source is removed with a note rather than overwriting a user-set value).

Repro + Verification

Environment

  • OS: macOS 26.3 (darwin)
  • Runtime/container: Node 22, local `pnpm`
  • Model/provider: N/A (config migration, no provider calls)
  • Integration/channel (if any): Telegram (issue source), but migration covers all channels.
  • Relevant config (redacted): see "Diagram" above.

Steps

  1. Place reaction keys under `channels.telegram.messages.*` (the shape from #67859).
  2. Run `openclaw doctor --fix`.
  3. Re-open `~/.openclaw/openclaw.json`.

Expected

  • `channels.telegram.messages` is gone.
  • `channels.telegram.ackReaction` carries the prior `ackReaction`.
  • `messages.ackReactionScope`, `messages.removeAckAfterReply`, `messages.statusReactions` carry the prior global-only values.
  • Reactions and status reactions now fire per the documented contract.

Actual

  • Matches expected via the new unit suite (see "Evidence").

Evidence

  • Failing test/log before + passing after: `pnpm test src/commands/doctor/shared/legacy-config-migrations.channel-messages.test.ts` → 7 tests pass (new file).
  • Adjacent regression coverage still green: `pnpm test src/commands/doctor/shared/legacy-config-migrate.test.ts src/commands/doctor-legacy-config.migrations.test.ts` → 57 tests pass, `pnpm test src/commands/doctor-config-flow.test.ts` → 40 tests pass.
  • Full directory: `pnpm test src/commands/doctor/shared` → 45 tests pass (commands lane).
  • Drift checks: `pnpm config:docs:check` OK, `pnpm plugin-sdk:api:check` OK (no config schema or SDK surface change).
  • `pnpm check` (lint + tsgo + import-cycles + madge) → green.

Human Verification (required)

  • Verified scenarios: Channel-level misplaced block (reporter's shape); account-level misplaced block; destination-already-set (honored, not clobbered); unknown third-party keys under the misplaced block (preserved); non-record top-level `messages` (refused, channel-scoped keys still repaired); `channels.defaults` / `channels.modelByChannel` (skipped — they are not channel entries).
  • Edge cases checked: Empty `messages` after migration is dropped; partial moves leave the container in place when unknown keys remain.
  • What you did not verify: Live gateway roundtrip against the Telegram bot on the reporter's account (not possible from the PR branch without their token). The runtime paths that consume the migrated config (`cfg.messages?.ackReactionScope`, `cfg.messages?.statusReactions`, `resolveAckReaction` channel / global fallback) are already covered by existing extension tests (e.g. `extensions/telegram/src/bot.create-telegram-bot.test.ts` — "reacts to mention-gated group messages when ackReaction is enabled" passes at HEAD when the config lives at top-level `messages.*`).

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 (non-breaking; existing correct configs untouched).
  • Config/env changes? No schema change; doctor adds a new repair path.
  • Migration needed? No manual step; `openclaw doctor --fix` handles affected configs.

Risks and Mitigations

  • Risk: A global-only key (for example `statusReactions`) placed under a specific channel now lifts to top-level `messages`, which applies globally to all channels. That matches the documented contract, but a user who intended it to be channel-scoped may be surprised.
    • Mitigation: The misplaced location was never wired to runtime, so prior behavior was "no effect" everywhere — lifting to top-level is strictly closer to the user's apparent intent and the only place the runtime reads from. Doctor emits an explicit change line for every move ("Moved channels.X.messages.Y → messages.Y."), and existing destination values are honored rather than overwritten.
  • Risk: Third-party plugin config nesting a `messages` block under a channel could look like misplacement.
    • Mitigation: The migration only touches keys in the known `MessagesConfig` set; unknown keys are preserved in place, and the `messages` container is only deleted when it is empty after migration.

Tested: fully tested (new unit suite + adjacent regression suites + full `pnpm check`). AI-assisted.

Made with Cursor

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/gateway/doctor.md (modified, +1/-0)
  • src/commands/doctor/shared/legacy-config-migrations.channel-messages.test.ts (added, +254/-0)
  • src/commands/doctor/shared/legacy-config-migrations.channel-messages.ts (added, +222/-0)
  • src/commands/doctor/shared/legacy-config-migrations.ts (modified, +2/-0)

Code Example

"channels": {
  "telegram": {
    "messages": {
      "ackReaction": "👀",
      "ackReactionScope": "all",
      "removeAckAfterReply": true,
      "statusReactions": {
        "enabled": true,
        "emojis": {
          "thinking": "🤔",
          "tool": "👨‍💻",
          "coding": "✍",
          "web": "🔥",
          "done": "👍",
          "error": "💔",
          "stallSoft": "😴",
          "stallHard": "😱",
          "compacting": "🤓"
        }
      }
    }
  }
}

---

curl -X POST "https://api.telegram.org/bot<token>/setMessageReaction" \
    -H "Content-Type: application/json" \
    -d '{"chat_id": <id>, "message_id": <id>, "reaction": [{"type": "emoji", "emoji": "👍"}]}'
{"ok":true,"result":true}
RAW_BUFFERClick to expand / collapse

Environment

  • OpenClaw version: 2026.4.5 (3e72c03)
  • Platform: Ubuntu 22.04 (VPS)
  • Channel: Telegram (bot as group admin)
  • Model: openai-codex/gpt-5.4

Config

"channels": {
  "telegram": {
    "messages": {
      "ackReaction": "👀",
      "ackReactionScope": "all",
      "removeAckAfterReply": true,
      "statusReactions": {
        "enabled": true,
        "emojis": {
          "thinking": "🤔",
          "tool": "👨‍💻",
          "coding": "✍",
          "web": "🔥",
          "done": "👍",
          "error": "💔",
          "stallSoft": "😴",
          "stallHard": "😱",
          "compacting": "🤓"
        }
      }
    }
  }
}

Expected behavior

  • Bot reacts with 👀 when receiving a message (ack)
  • Reaction changes to 🤔/👨‍💻 during processing
  • Final reaction is 👍 (done) or 💔 (error)

Actual behavior

  • No reactions at all. Zero. Silent.
  • Config validates successfully (openclaw config validate → valid)
  • No errors in logs related to reactions

Confirmed working

  • setMessageReaction API works when called manually via curl:
    curl -X POST "https://api.telegram.org/bot<token>/setMessageReaction" \
      -H "Content-Type: application/json" \
      -d '{"chat_id": <id>, "message_id": <id>, "reaction": [{"type": "emoji", "emoji": "👍"}]}'
    → {"ok":true,"result":true}
  • Bot is admin in the group with can_manage_chat: true
  • All emojis used are valid Telegram reaction emojis

Related issues

  • #45906 — statusReactions shows error emoji even for success (implies it DOES fire for some users)
  • #27073 — reactions fail silently on long messages (closed/stale)

Question

Is channels.telegram.messages the correct path for this config? Or does it belong elsewhere (e.g., agents.defaults.messages)? We tried agents.defaults.messages but got "Unrecognized key" validation error.

extent analysis

TL;DR

The issue is likely due to an incorrect configuration path for the Telegram reaction settings, and verifying the correct path is necessary to resolve the issue.

Guidance

  • Review the OpenClaw documentation to confirm the correct configuration path for Telegram reaction settings, as the current path channels.telegram.messages may not be accurate.
  • Check if there are any other configuration options or overrides that could be affecting the reaction settings, such as agents.defaults.messages or other channel-specific settings.
  • Verify that the statusReactions setting is enabled and properly configured, as it may be related to the issue.
  • Test the reaction settings with a minimal configuration to isolate the issue and determine if it's specific to the current setup.

Example

No code snippet is provided as it's not clearly supported by the issue, but an example of a minimal configuration for testing could be:

"channels": {
  "telegram": {
    "messages": {
      "ackReaction": "👀",
      "statusReactions": {
        "enabled": true
      }
    }
  }
}

Notes

The issue may be related to the statusReactions setting, which is mentioned in a related issue (#45906). Additionally, the fact that the setMessageReaction API works manually via curl suggests that the issue is likely with the configuration rather than the Telegram API.

Recommendation

Apply a workaround by testing different configuration paths and settings to determine the correct configuration for the Telegram reaction settings, as the current path may not be accurate.

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…

FAQ

Expected behavior

  • Bot reacts with 👀 when receiving a message (ack)
  • Reaction changes to 🤔/👨‍💻 during processing
  • Final reaction is 👍 (done) or 💔 (error)

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Telegram] statusReactions + ackReaction not firing on OpenClaw 2026.4.5 [1 pull requests, 1 participants]