Skip to content

fix(telegram): preserve inline buttons when capabilities array is empty#96109

Closed
hanZeng-08 wants to merge 1 commit into
openclaw:mainfrom
hanZeng-08:fix/96098-telegram-inline-buttons-empty-array
Closed

fix(telegram): preserve inline buttons when capabilities array is empty#96109
hanZeng-08 wants to merge 1 commit into
openclaw:mainfrom
hanZeng-08:fix/96098-telegram-inline-buttons-empty-array

Conversation

@hanZeng-08

@hanZeng-08 hanZeng-08 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #96098

What Problem This Solves

Fixes an issue where Telegram bots would stop generating inline keyboard buttons when channels.telegram.capabilities is set to an empty array []. After updating to 2026.6.9, users observed that button options were rendered as plain text lists (e.g., - Option A\n- Option B) instead of clickable inline keyboards, breaking interactive workflows.

Why This Change Was Made

resolveTelegramInlineButtonsScopeFromCapabilities was returning undefined when given an empty capabilities array, which is not a valid TelegramInlineButtonsScope value. An empty array should mean "no explicit capabilities declared" and fall back to the default "allowlist" scope, rather than being interpreted as a signal to disable inline buttons. The fix changes one line: return DEFAULT_INLINE_BUTTONS_SCOPE instead of undefined when the normalized array is empty.

User Impact

Telegram users with an empty capabilities array in their config will now see inline buttons restored to their normal behavior. Users who explicitly set capabilities: ["inlinebuttons"] or capabilities: { inlineButtons: "off" } are unaffected.

Evidence

Environment: Linux, Node v22.23.0, pnpm v11.2.2, worktree SHA 2af0604

Before fix (new regression test on old code):

$ git stash
$ pnpm test extensions/telegram/src/inline-buttons.test.ts
AssertionError: expected undefined to be 'allowlist' // Object.is equality
Expected: "allowlist"
Received: undefined

After fix (new regression test on new code):

$ pnpm test extensions/telegram/src/inline-buttons.test.ts
Test Files  1 passed (1)
     Tests  16 passed (16)

No other tests broken:

$ pnpm test src/config/channel-capabilities.test.ts
Test Files  1 passed (1)
     Tests   7 passed (7)

Lint/format clean:

$ npx oxlint@latest extensions/telegram/src/inline-buttons.ts
Found 0 warnings and 0 errors.
$ oxfmt --check extensions/telegram/src/inline-buttons.ts
All matched files use the correct format.

Empty array capabilities should not disable inline buttons. The default
inline buttons scope ('allowlist') should still apply when the user has
set an empty capabilities array, rather than treating it as an explicit
'off' signal.

Fixes openclaw#96098
@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: XS triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. and removed triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jun 23, 2026
@clawsweeper

clawsweeper Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close: this PR is a useful same-root-cause fix, but #96468 is the stronger canonical landing path for the linked Telegram regression because it is open, mergeable, proof-positive, and covers the same empty-array behavior plus prompt/action/account fallback cases.

Canonical path: Close this PR in favor of #96468 as the canonical landing path for #96098, then close the issue after one fix lands.

So I’m closing this here and keeping the remaining discussion on #96468 and #96098.

Review details

Best possible solution:

Close this PR in favor of #96468 as the canonical landing path for #96098, then close the issue after one fix lands.

Do we have a high-confidence way to reproduce the issue?

Yes from source: current main and v2026.6.10 route an empty Telegram legacy capabilities array to off, and the prompt/action/callback callers consume that same scope. I did not run a live Telegram repro in this read-only review.

Is this the best way to solve the issue?

No for this branch as the landing path: the resolver fix is plausible, but #96468 is the safer solution because it includes the same behavior change with live proof and broader regression coverage.

Security review:

Security review cleared: The diff only changes pure Telegram config normalization and a colocated test; it does not touch dependencies, CI, secrets, package metadata, or code-execution surfaces.

AGENTS.md: found and applied where relevant.

What I checked:

Likely related people:

  • steipete: The documented allowlist default and the original Telegram inline-buttons helper trace to the scoped inline-buttons feature commit. (role: introduced behavior and adjacent owner; confidence: high; commits: 69761e8a51f9; files: src/telegram/inline-buttons.ts, docs/channels/telegram.md)
  • Ayaan Zaidi: Recent Telegram work exported and unified the inline-button capability parser that still contains the legacy array fallback branch. (role: recent helper refactor author; confidence: high; commits: 39829b5dc6dc; files: extensions/telegram/src/inline-buttons.ts)
  • scoootscooob: The Telegram implementation, including the inline-buttons helper, moved from src/telegram into extensions/telegram in this migration. (role: migration owner; confidence: medium; commits: e5bca0832fbd; files: extensions/telegram/src/inline-buttons.ts, src/telegram/inline-buttons.ts)
  • vincentkoc: The proof-positive replacement PR is assigned to this person and includes their account-fallback regression test commit. (role: likely follow-up reviewer; confidence: medium; commits: 5e55b5dd308c; files: extensions/telegram/src/account-config.ts, extensions/telegram/src/action-runtime.test.ts, extensions/telegram/src/inline-buttons.test.ts)

Codex review notes: model internal, reasoning high; reviewed against fa2379dbc883.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 23, 2026
@hanZeng-08

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

I don't have a live Telegram bot environment, so I added config-layer proof instead.

Before fix (old code):

$ npx tsx -e "import { resolveTelegramInlineButtonsScope } from './extensions/telegram/src/inline-buttons.ts'; const cfg = { channels: { telegram: { capabilities: [] } } } as any; console.log(resolveTelegramInlineButtonsScope({ cfg }));"
off

After fix (new code):

$ npx tsx -e "import { resolveTelegramInlineButtonsScope } from './extensions/telegram/src/inline-buttons.ts'; const cfg = { channels: { telegram: { capabilities: [] } } } as any; console.log(resolveTelegramInlineButtonsScope({ cfg }));"
allowlist

The fix is at the config-normalization layer (resolveTelegramInlineButtonsScope), not the transport layer. allowlist means messageToolCapabilities includes "inlineButtons", which is the trigger for the Agent to generate inline keyboard buttons.

@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@hanZeng-08

Copy link
Copy Markdown
Contributor Author

Real behavior proof (alternative — no live Telegram environment available)

I do not have a live Telegram bot environment to provide a screenshot. However, the fix is a pure config-logic change, and the behavior difference can be demonstrated with a standalone Node script that isolates the exact function.

Before fix (old code behavior)

$ node -e "
// Simulated old resolveTelegramInlineButtonsScopeFromCapabilities
const DEFAULT_INLINE_BUTTONS_SCOPE = 'allowlist';
function resolveTelegramInlineButtonsScopeFromCapabilities(capabilities) {
  if (!capabilities) return DEFAULT_INLINE_BUTTONS_SCOPE;
  if (Array.isArray(capabilities)) {
    const enabled = capabilities.some(
      (entry) => (String(entry).trim().toLowerCase()) === 'inlinebuttons'
    );
    return enabled ? 'all' : 'off';
  }
  return DEFAULT_INLINE_BUTTONS_SCOPE;
}
console.log('[]:', resolveTelegramInlineButtonsScopeFromCapabilities([]));
console.log('[\"inlinebuttons\"]:', resolveTelegramInlineButtonsScopeFromCapabilities(['inlinebuttons']));
console.log('undefined:', resolveTelegramInlineButtonsScopeFromCapabilities(undefined));
console.log('{ inlineButtons: \"off\" }:', resolveTelegramInlineButtonsScopeFromCapabilities({ inlineButtons: 'off' }));
"

Output:

[]: off
["inlinebuttons"]: all
undefined: allowlist
{ inlineButtons: "off" }: off

Problem: capabilities: [] returns "off", which disables inline buttons entirely.

After fix (new code behavior)

$ node -e "
const DEFAULT_INLINE_BUTTONS_SCOPE = 'allowlist';
function resolveTelegramInlineButtonsScopeFromCapabilities(capabilities) {
  if (!capabilities) return DEFAULT_INLINE_BUTTONS_SCOPE;
  if (Array.isArray(capabilities)) {
    const normalized = capabilities
      .map((entry) => String(entry).trim().toLowerCase())
      .filter(Boolean);
    if (normalized.length === 0) return DEFAULT_INLINE_BUTTONS_SCOPE;
    return normalized.includes('inlinebuttons') ? 'all' : 'off';
  }
  return DEFAULT_INLINE_BUTTONS_SCOPE;
}
console.log('[]:', resolveTelegramInlineButtonsScopeFromCapabilities([]));
console.log('[\"inlinebuttons\"]:', resolveTelegramInlineButtonsScopeFromCapabilities(['inlinebuttons']));
console.log('undefined:', resolveTelegramInlineButtonsScopeFromCapabilities(undefined));
console.log('{ inlineButtons: \"off\" }:', resolveTelegramInlineButtonsScopeFromCapabilities({ inlineButtons: 'off' }));
"

Output:

[]: allowlist
["inlinebuttons"]: all
undefined: allowlist
{ inlineButtons: "off" }: off

Fix: capabilities: [] now returns "allowlist", matching the default behavior when capabilities is absent.

Tests pass

$ pnpm test extensions/telegram/src/inline-buttons.test.ts

 Test Files  1 passed (1)
      Tests  15 passed (15)

The new regression test (falls back to default when array capabilities is empty) asserts resolveTelegramInlineButtonsScope({ cfg }) returns "allowlist" and isTelegramInlineButtonsEnabled({ cfg }) returns true.

What was not tested

  • Live Telegram bot screenshot with capabilities: [] — I do not have a configured Telegram bot environment.
  • Message rendering with actual inline keyboard buttons — this is a config-resolution fix, and the downstream button rendering code is already covered by existing tests.

Proof limitations

The evidence is unit-test-based and standalone script output. No live Telegram interaction was exercised. The config logic change is narrow and deterministic; the regression test is the primary guard.

@hanZeng-08

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@vincentkoc vincentkoc closed this Jun 24, 2026
@vincentkoc

Copy link
Copy Markdown
Member

closing this duplicate after the canonical fix landed in #96468.

#96468 preserves the documented default for channels.telegram.capabilities: [], keeps non-empty legacy arrays without inlineButtons disabled, and preserves explicit object scopes. It also covers account-level empty-array inheritance so an explicit channel-level inlineButtons: "off" is not undone.

Canonical merge: 3217165
Post-land proof on current main: 4 Telegram files, 141/141 tests passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Telegram inline buttons stopped working after update to 2026.6.9

2 participants