Skip to content

fix: /pair qr fails to show QR code in chat surfaces#97933

Merged
joshavant merged 3 commits into
mainfrom
fix-pair-qr-surface-rendering
Jun 29, 2026
Merged

fix: /pair qr fails to show QR code in chat surfaces#97933
joshavant merged 3 commits into
mainfrom
fix-pair-qr-surface-rendering

Conversation

@joshavant

Copy link
Copy Markdown
Contributor

Related: user report from Telegram/TUI/ControlUI pairing flow.

What Problem This Solves

Fixes an issue where users running /pair qr could fail to get a useful QR code in chat surfaces when the command was routed through the gateway-backed reply path. The same pairing command could show Media failed, no visible QR image, or no TUI-readable QR even though the setup code itself was generated.

Why This Change Was Made

The fix keeps native media-capable channel sends on the existing temporary PNG path, but moves the gateway fallback off sensitive data:image/png media URLs and onto typed pairing QR channel data. The gateway now builds a live-only openclaw_pairing_qr content block with both an image URL and terminal QR text, while persisted history is recomputed without the sensitive QR block; TUI and ControlUI render that block directly.

User Impact

Users can now run /pair qr and get a useful QR code in Telegram, the TUI, and the ControlUI dashboard. The pairing QR remains single-use and live-only rather than being stored in durable chat history.

Evidence

AI-assisted: yes, Codex.

  • Formatting: ./node_modules/.bin/oxfmt --check --threads=1 extensions/device-pair/index.ts extensions/device-pair/index.test.ts src/auto-reply/reply-payload.ts src/plugin-sdk/reply-payload.ts src/plugin-sdk/reply-payload.test.ts src/gateway/server-methods/chat.ts src/gateway/server-methods/chat.directive-tags.test.ts src/tui/tui-formatters.ts src/tui/tui-stream-assembler.test.ts ui/src/ui/chat/grouped-render.ts ui/src/ui/chat/grouped-render.test.ts
  • Focused tests: node scripts/run-vitest.mjs src/plugin-sdk/reply-payload.test.ts extensions/device-pair/index.test.ts src/gateway/server-methods/chat.directive-tags.test.ts src/gateway/server-methods/chat-reply-media.test.ts src/gateway/server-methods/chat-webchat-media.test.ts src/tui/tui-stream-assembler.test.ts src/tui/tui-event-handlers.test.ts ui/src/ui/chat/grouped-render.test.ts src/media/qr-terminal.test.ts src/media/qr-terminal.render.test.ts src/cli/qr-cli.test.ts src/cli/qr-dashboard.integration.test.ts src/pairing/setup-code.test.ts
  • Diff sanity: git diff --check
  • Plugin SDK baseline: node --max-old-space-size=8192 --import tsx scripts/generate-plugin-sdk-api-baseline.ts --check
  • Auto review: .agents/skills/autoreview/scripts/autoreview --mode local returned clean after refreshing the plugin SDK API baseline.
  • Live fix proof: Convex telegram credential lease used the telegram kind, not telegram-user; real Telegram sendPhoto was observed, the caption included scan instructions, and no Media failed text was emitted.
  • Live fix proof, gateway surfaces: webchat fallback emitted pairing QR channel data with no legacy mediaUrl; TUI output included terminal QR block glyphs and instructions; ControlUI rendered one data-PNG image with alt text OpenClaw pairing QR code.
  • Live regression repro: parent commit 15de9d881a8 reproduced the legacy broken shape with data:image/png in mediaUrl and no pairing QR channel data.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: web-ui App: web-ui gateway Gateway runtime extensions: device-pair size: M maintainer Maintainer-authored PR labels Jun 29, 2026
@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 7:09 PM ET / 23:09 UTC.

Summary
The PR changes device-pair /pair qr webchat fallback to send private pairing QR channel data, has the gateway build live-only QR display blocks, and teaches TUI/Control UI to render those blocks without persisting the QR content.

PR surface: Source +141, Tests +131. Total +272 across 11 files.

Reproducibility: yes. Current main's device-pair webchat branch returns a sensitive data:image/png media URL while the gateway display path excludes sensitive media; the PR body also reports a parent-commit live regression repro.

Review metrics: 2 noteworthy metrics.

  • Public Plugin SDK surface: 0 added, 0 changed, 0 removed at latest head. The previous SDK-expansion blocker was removed by the latest commit, keeping pairing QR metadata out of third-party plugin API.
  • Sensitive live display blocks: 1 internal block type added. The new openclaw_pairing_qr block carries setup-code-derived QR content, so non-persistence and live-only handling matter before merge.

Stored data model
Persistent data-model change detected: vector/embedding metadata: src/auto-reply/reply-payload.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: canonical
Canonical: #97933
Summary: This PR is the active candidate for the /pair qr chat-surface rendering bug; older/broader pairing QR work overlaps but does not replace it.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] The PR carries setup-code-derived QR material through a new live-only gateway display block, so maintainers should explicitly accept the private block/non-persistence contract before merge.

Maintainer options:

  1. Land after live-only boundary review (recommended)
    Accept the private openclaw_pairing_qr display block once maintainers confirm the QR is only broadcast live and the sanitized persistence path remains covered.
  2. Generalize sensitive display first
    If maintainers expect more sensitive QR/code surfaces soon, replace the pairing-specific block with a generic live-only presentation seam before merge.
  3. Pause if pairing should move to RPC
    Pause this PR only if maintainers want chat-surface /pair qr behavior folded into the broader non-terminal pairing RPC direction instead.

Next step before merge

  • [P2] Manual maintainer review remains appropriate because the protected maintainer label and sensitive live QR display boundary need human acceptance, not a narrow automated repair.

Security
Cleared: No concrete security defect was found; the sensitive QR live-display path has non-persistence coverage, with maintainer merge risk tracked separately.

Review details

Best possible solution:

Land the current internal live-only QR rendering shape after maintainer review confirms the sensitive QR block remains broadcast-only and current head checks are green.

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

Yes. Current main's device-pair webchat branch returns a sensitive data:image/png media URL while the gateway display path excludes sensitive media; the PR body also reports a parent-commit live regression repro.

Is this the best way to solve the issue?

Yes, with maintainer review. The latest head is the narrowest maintainable shape I found because it keeps the QR metadata internal, renders live-only QR blocks for chat surfaces, and recomputes persisted history without QR content.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add merge-risk: 🚨 security-boundary: Merging introduces a live display path for setup-code-derived QR material, which needs maintainer confidence that it is not persisted or exposed beyond the intended chat broadcast.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body supplies after-fix live-output proof for Telegram media send, webchat pairing QR channel data, TUI terminal QR output, Control UI QR rendering, and a parent-commit regression repro.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove merge-risk: 🚨 compatibility: Current PR review merge-risk labels are merge-risk: 🚨 security-boundary.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The PR targets a user-facing pairing workflow where Telegram, TUI, or Control UI users can otherwise fail to receive a usable onboarding QR.
  • merge-risk: 🚨 security-boundary: Merging introduces a live display path for setup-code-derived QR material, which needs maintainer confidence that it is not persisted or exposed beyond the intended chat broadcast.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body supplies after-fix live-output proof for Telegram media send, webchat pairing QR channel data, TUI terminal QR output, Control UI QR rendering, and a parent-commit regression repro.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies after-fix live-output proof for Telegram media send, webchat pairing QR channel data, TUI terminal QR output, Control UI QR rendering, and a parent-commit regression repro.
Evidence reviewed

PR surface:

Source +141, Tests +131. Total +272 across 11 files.

View PR surface stats
Area Files Added Removed Net
Source 6 147 6 +141
Tests 5 134 3 +131
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 11 281 9 +272

What I checked:

  • Repository policy applied: Root AGENTS.md and scoped guides for extensions, gateway, TUI, Control UI, and Plugin SDK were read and applied to the owner-boundary and proof review. (AGENTS.md:1, b7b2e1f77eb4)
  • Current main broken shape: Current main's device-pair webchat fallback returns a generated QR as mediaUrl with sensitiveMedia: true, which is the path that gateway chat later excludes from display media. (extensions/device-pair/index.ts:836, b7b2e1f77eb4)
  • Current gateway excludes sensitive media: Current main only treats sensitive media payloads as media-bearing and skips sensitive media when building assistant display content, so the webchat QR can be omitted from visible chat surfaces. (src/gateway/server-methods/chat.ts:858, b7b2e1f77eb4)
  • Latest head keeps pairing metadata private: The latest head removed the earlier openclaw/plugin-sdk/reply-payload export path and builds the device-pair channel envelope from a local helper instead. (extensions/device-pair/index.ts:865, aba3a0ae7410)
  • Internal reader is not Plugin SDK surface: The shared reader is documented as a private device-pair-to-gateway live-display envelope and was not present in the latest PR file list under src/plugin-sdk. (src/auto-reply/reply-payload.ts:65, aba3a0ae7410)
  • Live display and non-persistence path: The gateway builds openclaw_pairing_qr blocks only when live sensitive display is requested, while the persisted sensitive-media branch recomputes assistant content without that display flag. (src/gateway/server-methods/chat.ts:950, aba3a0ae7410)

Likely related people:

  • BunsDev: Authored merged PR fix(pair): render /pair qr as media #70047, which changed the same /pair qr media rendering path in device-pair and gateway chat. (role: introduced earlier QR media path; confidence: high; commits: 43a941b51c16; files: extensions/device-pair/index.ts, src/gateway/server-methods/chat.ts, src/plugin-sdk/reply-payload.ts)
  • mbelinky: Authored merged iOS/device-pairing work that added and stabilized QR pairing flows used by this command family. (role: adjacent device-pairing contributor; confidence: medium; commits: 130e59a9c096, 730f86dd5c43; files: extensions/device-pair/index.ts, apps/ios/Sources/Onboarding/QRScannerView.swift)
  • harjothkhara: Authored merged PR fix(ui): collapse non-terminal internal tool errors #90122, which recently changed the Control UI grouped chat renderer extended by this PR. (role: recent Control UI chat renderer contributor; confidence: medium; commits: 355c43fe0c9e; files: ui/src/ui/chat/grouped-render.ts, ui/src/ui/chat/grouped-render.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. 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 29, 2026
@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Jun 29, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 29, 2026
@joshavant joshavant self-assigned this Jun 29, 2026
@openclaw-barnacle openclaw-barnacle Bot removed docs Improvements or additions to documentation scripts Repository scripts labels Jun 29, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 29, 2026
@joshavant

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 29, 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.

@joshavant
joshavant merged commit 052dc59 into main Jun 29, 2026
122 of 135 checks passed
@joshavant
joshavant deleted the fix-pair-qr-surface-rendering branch June 29, 2026 23:17
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 30, 2026
* fix(pair): render qr per chat surface

* test(plugin-sdk): update reply helper surface budget

* fix(pair): keep qr metadata internal
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
* fix(pair): render qr per chat surface

* test(plugin-sdk): update reply helper surface budget

* fix(pair): keep qr metadata internal
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
* fix(pair): render qr per chat surface

* test(plugin-sdk): update reply helper surface budget

* fix(pair): keep qr metadata internal
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
* fix(pair): render qr per chat surface

* test(plugin-sdk): update reply helper surface budget

* fix(pair): keep qr metadata internal
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui extensions: device-pair gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant