You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tui: /pair QR code encodes the loopback 127.0.0.1 link, so other devices cannot connect #58810
The QR code shown by /pair in the TUI encodes the loopback link, so no other device can connect.
/pair renders links[0], i.e. the first entry of server.info().urls, which is the loopback address (127.0.0.1). The same machine's opencode pair CLI does filter loopback addresses out of the QR payload, so the two entry points behave differently.
The dialog should encode the reachable (non-loopback) addresses, or omit the QR when only loopback is available, like the CLI does.
Related: #54051 (open PR fixing this in packages/tui/src/component/dialog-pair.tsx).
Separately, the CLI QR encodes {"code","urls"} JSON, which generic phone cameras render as text instead of an openable link — tracked in #54871.
Plugins
None configured (only a @opencode-ai/plugin dependency in ~/.config/opencode/package.json).
OpenCode version
v2.1.4 (latest channel)
Steps to reproduce
Run opencode and open /pair in the TUI.
Scan the QR code with a phone (or decode it with a standard QR decoder).
The payload is http://127.0.0.1:<port>/auth/connect/<code>, which is unreachable from the phone.
Expected behavior
The QR encodes an address reachable from other devices on the LAN (or the remote tunnel URL when remote access is enabled), and 127.0.0.1 is not what gets scanned.
Actual behavior
QR decodes to http://127.0.0.1:<port>/auth/connect/<code> even when the server is reachable at 192.168.x.x:<port> and the CLI lists those URLs.
Operating System
Windows 11 Pro 10.0.26300
Terminal
VS Code integrated terminal (TERM_PROGRAM=vscode), shell: cmd.exe
Additional context
opencode pair on the same machine prints http://127.0.0.1:..., http://192.168.1.146:..., etc., and its QR only carries the non-loopback URLs.
Workaround: use the LAN link printed by opencode pair manually.
Thanks for the detailed report. I reproduced this on v2.1.4, and it still happens on the latest dev (13a615b).
Steps:
Bind the service to 0.0.0.0. server.info().urls then lists http://127.0.0.1:<port> first, followed by the LAN address.
Open /pair in the TUI.
Decode the QR code. It decodes to http://127.0.0.1:<port>/auth/connect/<code>, even though the LAN link is listed right next to it.
The dialog also still shows the "Run opencode service set hostname 0.0.0.0" hint when the service is already bound to 0.0.0.0.
The cause is that dialog-pair.tsx builds the QR from the first link, while opencode pair filters out loopback addresses first. Open PR #54051 addresses this. #54871 is the separate issue about the CLI QR containing JSON instead of a plain URL.
The recording and screenshots below show the dialog, and the script below reproduces it. Until this is fixed, opening the LAN link from opencode pair (or from the dialog's link list) works.
recording.mp4
Reproduction script (drive.ts)
// Reproduces #58810: the TUI /pair QR code encodes the loopback link.// The service is bound to 0.0.0.0 (via a wrapper), so server.info() lists// 127.0.0.1 first, then the LAN address(es).import{Effect}from"effect"import{OpenCodeDriver}from"opencode-drive"import{chmodSync,writeFileSync}from"node:fs"// Wrapper that runs opencode (release from PATH, or the checkout in $OC_DEV) and// binds the service to 0.0.0.0, as `opencode service set hostname 0.0.0.0` would.constwrapper="/tmp/opencode-58810-wrap.sh"writeFileSync(wrapper,"#!/usr/bin/env bash\nif [ -n \"$OC_DEV\" ]; then\n run=(bun --conditions=browser --preload=\"$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js\" \"$OC_DEV/packages/cli/src/index.ts\")\nelse\n run=(opencode)\nfi\nif [ \"$1\" = \"serve\" ]; then shift; exec \"${run[@]}\" serve --hostname 0.0.0.0 \"$@\"; fi\nexec \"${run[@]}\" \"$@\"\n")chmodSync(wrapper,0o755)exportdefaultOpenCodeDriver.use({opencode: {command: [wrapper]},tui: {recording: true,keypressOverlay: true,viewport: {cols: 140,rows: 50}},},({ ui, opencode })=>Effect.gen(function*(){constinfo: any=yield*opencode.server.info()yield*Effect.log(`server.info().urls = ${JSON.stringify(info.urls)}`)yield*ui.screenshot("home")yield*ui.type("/pair")yield*Effect.sleep(500)yield*ui.enter()yield*Effect.sleep(1500)yield*ui.screenshot("after-enter")yield*ui.waitFor("Links",{timeout: 15000}asany)yield*Effect.sleep(1500)constframe: any=yield*ui.capture()yield*Effect.log(`screen:\n${frame.lines.map((l: any)=>typeofl==="string" ? l : (l.text??JSON.stringify(l))).join("\n")}`)yield*ui.screenshot("pair-dialog")}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
opencode-agent commentedon Oct 24, 2026
Thanks for the detailed report. I reproduced this on v2.1.4, and it still happens on the latest
dev(13a615b).Steps:
0.0.0.0.server.info().urlsthen listshttp://127.0.0.1:<port>first, followed by the LAN address./pairin the TUI.http://127.0.0.1:<port>/auth/connect/<code>, even though the LAN link is listed right next to it.The dialog also still shows the "Run
opencode service set hostname 0.0.0.0" hint when the service is already bound to0.0.0.0.The cause is that
dialog-pair.tsxbuilds the QR from the first link, whileopencode pairfilters out loopback addresses first. Open PR #54051 addresses this. #54871 is the separate issue about the CLI QR containing JSON instead of a plain URL.The recording and screenshots below show the dialog, and the script below reproduces it. Until this is fixed, opening the LAN link from
opencode pair(or from the dialog's link list) works.recording.mp4
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.