#19548: fix(android): show scaffold instead of raw JSON on canvas auth errors
app: android
size: XS
Cluster:
Android Ed25519 Enhancements
## Summary
Describe the problem and fix in 2–5 bullets:
- **Problem:** When the Android app connects, the WebView sometimes loads the canvas URL before authentication is fully established. This results in a raw JSON 401 error displayed to the user:
`{"error":{"message":"Unauthoriz ed","type":"unauthorized"}}`.
- **Why it matters:** It looks broken/scary to users on first launch or reconnection, even though the app is actually working in the background.
- **What changed:** The `WebViewClient` now intercepts HTTP 401/403 errors and generic network errors on the main frame. Instead of showing the raw error, it loads the local `scaffold.html` (blank canvas).
The app automatically re-navigates to the correct URL once the session is ready.
- **What did NOT change:** No changes to authentication logic or retry mechanisms; purely a UI fallback for the WebView.
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [ ] Integrations
- [ ] API / contracts
- [x] UI / DX (Android)
- [ ] CI/CD / infra
## Linked Issue/PR
- Closes (none)
## User-visible / Behavior Changes
- Users will no longer see raw JSON error text on the home screen during connection/auth glitches.
- They will see a neutral blank canvas (scaffold) until the app connects and loads the real UI.
## Security Impact (required)
- New permissions/capabilities? (No)
- Secrets/tokens handling changed? (No)
- New/changed network calls? (No)
- Command/tool execution surface changed? (No)
- Data access scope changed? (No)
## Repro + Verification
### Environment
- OS: Android 15 (OnePlus 12)
- Runtime: OpenClaw Android App
- Gateway: Local dev instance
### Steps
1. Launch app with a valid gateway configured.
2. Force a race condition (or simulate a 401 by invalidating the token temporarily).
3. Observe the WebView behavior.
### Expected
- WebView shows blank scaffold or loading state.
### Actual
- Before: Raw JSON `{"error":...}` text.
- After: Clean scaffold UI.
## Evidence
-Previously, error would persist even after resolution:
<img width="1438" height="591" alt="image" src="https://github.com/user-attachments/assets/f490154c-9a54-4ce4-a0de-b8ddbf0899cb" />
-Now:
<img width="1438" height="564" alt="image" src="https://github.com/user-attachments/assets/9ae85865-b805-497a-8e58-bc671e3e96e0" />
## Human Verification (required)
What you personally verified (not just CI), and how:
- Verified on OnePlus 12.
- Simulating auth error triggers the fallback path correctly.
## Compatibility / Migration
- Backward compatible? (Yes)
🤖 AI-Assisted: Yes (Claude via OpenClaw)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes a UX issue where Android users would see raw JSON error text (`{"error":{"message":"Unauthorized","type":"unauthorized"}}`) in the WebView during auth race conditions on app start. The fix intercepts HTTP 401/403 errors in `onReceivedHttpError` and all main-frame network errors in `onReceivedError`, loading the local `scaffold.html` as a neutral blank canvas instead. Re-navigation to the correct URL happens through the existing `CanvasController.navigate()` mechanism once the gateway session is established — this PR does not change that flow.
**Key changes:**
- `onReceivedError`: now loads `scaffold.html` for all main-frame network errors (gateway offline, timeout, etc.) instead of only logging in debug builds.
- `onReceivedHttpError`: now loads `scaffold.html` for 401/403 responses instead of only logging in debug builds.
- Logging is correctly preserved behind the `isDebuggable` guard.
**Minor concern:**
- The scaffold asset URL `"file:///android_asset/CanvasScaffold/scaffold.html"` is now duplicated as a string literal in two places in `RootScreen.kt`, while `CanvasController` already holds this string as `private val scaffoldAssetUrl`. Promoting the constant to a `companion object` would keep it DRY across both classes.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge — it is a purely additive UI fallback with no changes to auth logic, token handling, or network calls.
- The change is narrow in scope (two callbacks in one file), does not affect authentication or re-connection logic, and the scaffold fallback is already used by `CanvasController` for the default canvas state. The main-frame guard ensures sub-resource errors are unaffected. The only finding is a style-level duplication of the scaffold URL constant. No logic errors or security issues were identified.
- No files require special attention beyond the minor duplicate-constant concern in `apps/android/app/src/main/java/ai/openclaw/android/ui/RootScreen.kt`.
<sub>Last reviewed commit: 205f446</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#13321: android/gateway: harden manual connect identity and A2UI UX
by m888m · 2026-02-10
76.4%
#11205: Android: fix gateway connection and canvas URL for Tailscale serve
by emonty · 2026-02-07
75.9%
#11740: fix(gateway): remove IP-based canvas auth fallback
by coygeek · 2026-02-08
73.5%
#5867: Android: Add BouncyCastle Ed25519 fallback and gateway token UI
by brandonpollack23 · 2026-02-01
72.9%
#19814: Codex/align delimiter parsing assertion with parser
by Johnsonbros · 2026-02-18
72.2%
#15951: fix: Android production build permits cleartext traffic globally
by coygeek · 2026-02-14
71.8%
#2530: fix(gateway): improve auth error for native apps
by Episkey-G · 2026-01-27
71.6%
#21450: Android: allow HTTP for LAN hosts
by pedrochagasmaster · 2026-02-19
71.5%
#5693: fix(chat): display error messages when LLM requests fail
by niemesrw · 2026-01-31
71.4%
#20422: Fix/tailscale device pairing
by slagyr · 2026-02-18
71.3%