pingdotgg / pingdotgg/t3code

[Bug]: Android manual pairing rejects formatted codes and can report a false timeout

Open
#4,889 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
23k
Forks
5.9k
Avg merge
11h 14m
Merged PRs (30d)
357

Description

Before submitting
  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.
Area

apps/mobile (not currently listed in the issue template's Area options)

Steps to reproduce
  1. Run a headless T3 server and expose it through Tailscale Serve HTTPS.
  2. Create a one-time pairing credential. The CLI emits an uppercase, unhyphenated 12-character value.
  3. In the official Android app, open Add Environment.
  4. Enter the reachable HTTPS backend in Host.
  5. Enter the credential in the hyphenated format suggested by the field placeholder (for example, XXXX-XXXX-XXXX).
  6. Tap Add environment.
  7. Retry with the raw unhyphenated code.
  8. As a control, paste the complete https://<host>/pair#token=<raw-token> URL into Host, leave Pairing code empty, and submit.
Expected behavior

The manual pairing-code path should work reliably.

Either:

  • normalize presentation separators/whitespace and case before exchange, or
  • show the exact server-generated format and clearly require it.

The surfaced error should also match the actual failed request.

Actual behavior
  • Hyphenated manual entry is sent verbatim and the server returns 401 auth_invalid.
  • A retry with raw code also failed during this reproduction.
  • The app subsequently reported a timeout after 10000ms, despite the corresponding server-side /oauth/token request returning 401 in approximately 2.7ms.
  • Pasting the complete pairing URL into Host with Pairing code empty succeeded immediately.

The backend and transport were independently verified: environment discovery returned 200, Tailscale connectivity was healthy, and a disposable server-generated credential exchanged successfully through the same public HTTPS /oauth/token endpoint.

Impact

Major degradation or frequent failure.

The advertised manual host + pairing-code flow is unreliable. QR/full-URL pairing remains a workaround.

Version or commit
  • Server: t3 v0.0.32-nightly.20260729.951
  • Mobile: official Android client installed from Google Play on 2026-07-29
Environment
  • Device: Pixel 9a
  • Client OS: Android
  • Server OS: Linux x64
  • Transport: Tailscale Serve HTTPS on port 443
  • Mobile HTTP user agent observed server-side: okhttp/4.9.2
Logs or stack traces
GET  /.well-known/t3/environment -> 200
POST /oauth/token                  -> 401 (about 2.7ms)
Mobile UI                          -> timed out after 10000ms

Control:
POST /oauth/token with a disposable server-generated credential through
the same Tailscale HTTPS endpoint -> 200

Secrets, pairing credentials, environment IDs, trace IDs, and Tailnet hostnames have been omitted.

Relevant source observation

The Android pairing field currently shows the placeholder abc-123-xyz, while buildPairingUrl() only applies .trim() to the code before putting it into the URL fragment. It does not remove presentation separators or normalize case.

Relevant files:

  • apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx
  • apps/mobile/src/features/connection/pairing.ts
Workaround

Paste the complete server-generated /pair#token=... URL into the Host field, leave Pairing code empty, then add the environment. Scanning the server-generated QR code should follow the same successful path.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx and apps/mobile/src/features/connection/pairing.ts, focusing on buildPairingUrl() and the manual pairing request and error path. Reproduce the hyphenated and raw-code cases against the documented endpoint, then verify that accepted formatting reaches the server correctly and a fast 401 is not surfaced as a 10000ms timeout.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, typescript
Domain
authentication, mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.