fix(telegram): bypass sequentializer for approval callback_queries#64979
fix(telegram): bypass sequentializer for approval callback_queries#64979obviyus merged 3 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes a sequentializer deadlock on Telegram where approval Confidence Score: 5/5Safe to merge — targeted single-file fix that correctly resolves a real deadlock following the established lane-separation pattern. No P0/P1 findings. The fix is minimal, correct, follows existing patterns for abort and btw lanes, uses a properly exported SDK function, and is covered by 4 new targeted tests. No files require special attention. Reviews (1): Last reviewed commit: "fix(telegram): bypass sequentializer for..." | Re-trigger Greptile |
Approval callback_queries from clicking inline buttons get the same sequential key as the blocked agent turn (telegram:<chatId>), causing a deadlock: the callback can't run because the lane is held, and the lane can't release because it's waiting for the callback. Give approval callbacks a separate lane (telegram:<chatId>:approval), same pattern as abort requests (telegram:<chatId>:control) and btw requests (telegram:<chatId>:btw). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c074e85 to
76165ab
Compare
obviyus
left a comment
There was a problem hiding this comment.
Verified the Telegram approval deadlock path: approval button callback queries were sharing the blocked chat lane, and the PR fixes that by routing /approve ... callbacks onto a separate sequentializer key.
Maintainer follow-up: trimmed the added explanatory comments, rebased onto latest main, and added the unreleased changelog entry for this user-facing fix.
Local gate: pnpm test extensions/telegram/src/sequential-key.test.ts.
…anks @nk3750) * fix(telegram): bypass sequentializer for approval callback_queries Approval callback_queries from clicking inline buttons get the same sequential key as the blocked agent turn (telegram:<chatId>), causing a deadlock: the callback can't run because the lane is held, and the lane can't release because it's waiting for the callback. Give approval callbacks a separate lane (telegram:<chatId>:approval), same pattern as abort requests (telegram:<chatId>:control) and btw requests (telegram:<chatId>:btw). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(telegram): trim approval lane comments * fix: unblock Telegram approval callback deadlock (openclaw#64979) (thanks @nk3750) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Ayaan Zaidi <hi@obviy.us>
…anks @nk3750) * fix(telegram): bypass sequentializer for approval callback_queries Approval callback_queries from clicking inline buttons get the same sequential key as the blocked agent turn (telegram:<chatId>), causing a deadlock: the callback can't run because the lane is held, and the lane can't release because it's waiting for the callback. Give approval callbacks a separate lane (telegram:<chatId>:approval), same pattern as abort requests (telegram:<chatId>:control) and btw requests (telegram:<chatId>:btw). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(telegram): trim approval lane comments * fix: unblock Telegram approval callback deadlock (openclaw#64979) (thanks @nk3750) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Summary
Fixes #64977 — plugin approval buttons on Telegram deadlock because the Grammy sequentializer queues the approval
callback_querybehind the blocked agent turn.One file changed, one pattern followed. Same fix as abort requests (
telegram:<chatId>:control) and btw requests (telegram:<chatId>:btw).Root Cause
getTelegramSequentialKey()returns the same key (telegram:<chatId>) for both:plugin.approval.waitDecision)callback_queryfrom clicking the inline buttonThe
@grammyjs/runnersequentializer processes same-key updates sequentially. Deadlock: the callback can't run because the lane is held, and the lane can't release because it's waiting for the callback.Fix
Detect approval
callback_queryupdates viaparseExecApprovalCommandText(data)and return a separate sequential key:telegram:<chatId>:approval.This is the same pattern already used for:
telegram:<chatId>:control(line 40-44)telegram:<chatId>:btw(line 46-54)Before / After (gateway logs, same machine, same guardrail)
Before — approval times out after 300s, resolve arrives too late:
After — approval resolves in 3s:
Audit log confirms:
decision: "approval_required"at19:22:32,resolution: "allow-once",userResponse: "approved"at19:22:35.Changes
extensions/telegram/src/sequential-key.tsdata?: stringtocallback_querycontext type. Detect approval callbacks viaparseExecApprovalCommandTextand returntelegram:<chatId>:approvalkey.extensions/telegram/src/sequential-key.test.tsTest plan
pnpm test -- extensions/telegram/src/sequential-key.test.ts— 23/23 pass (19 existing + 4 new)telegram:<chatId>:approvalkeytelegram:<chatId>:approvalkeyalwaysalias callback →telegram:<chatId>:approvalkeytelegram:<chatId>key (no false positives)require_approval→ Telegram button click → resolves in 3s (was 300s timeout)Environment
requireApprovalfrombefore_tool_callguardrails)🤖 Generated with Claude Code