Skip to content

desktop: auto-login: window opens to manual login page because host fails to fetch local-auth token #121

Description

@sanjitkverma

What this prevents

  • I can launch the desktop app and have the window open already authenticated as the local user.
  • I can use the desktop client without configuring Google OAuth or manually creating an account.
  • I can close and relaunch the app and not have to log in again.

Every desktop launch should auto-inject a local user JWT into the webview so the user lands on the dashboard. Today the host's reqwest call to the desktop local-auth endpoint fails on every attempt for a reason that does not surface in the logs (the error chain is empty). Combined with the sidecar emitting TESSLATE_READY before uvicorn actually binds, the host can spend its retry budget before the sidecar is even accepting connections. The frontend has no fallback path. The result: every launch lands on the manual-login page, and Google login then fails because no GOOGLE_CLIENT_ID is configured by default. The user effectively cannot use the app without manual setup.


Surface

Two contributing root causes share the same user-facing symptom.

1. Persistent reqwest failure from the host

  • The desktop host's fetch_local_user_token fails every iteration even AFTER uvicorn is bound. Verified by curl: curl http://127.0.0.1:43111/api/desktop/local-auth returns 401 in 3 ms while the host's retry loop is still failing in the same process.
  • The same reqwest config used by the tray succeeds for /api/desktop/tray-state. Both create a fresh client with the same timeout, both target 127.0.0.1:43111, both pass an Authorization: Bearer ... header. The only behavioral difference is polling cadence (5 s vs 500 ms) and which endpoint they hit.
  • Error message from the host's reqwest has no underlying cause: error sending request for url (http://127.0.0.1:43111/api/desktop/local-auth) with no : ... suffix. This suggests reqwest::Error::source() returns None, which is unusual for a connect or send failure.

2. Sidecar emits readiness too early

  • The sidecar prints TESSLATE_READY from its entrypoint before uvicorn binds the listening socket, so the host begins its bounded retry budget against a port that is not yet accepting connections. Whatever extra latency uvicorn needs to start eats into that budget.

Expected vs Actual

Expected: on launch, the host obtains a local-auth token within the retry budget, injects it into the webview, and the user lands on the dashboard.

Actual: the host exhausts the retry loop and falls back to the manual-login page. Google login fails because no client id is configured. The app appears unusable.

Suggested fix

Two independent changes; either alone improves the situation, both together fix it.

  1. Make the sidecar emit TESSLATE_READY after uvicorn's startup hook fires (or after a successful self-loopback GET /healthz), not before. The bound socket is what the host actually needs.
  2. Log reqwest::Error::source() chain and is_connect() / is_timeout() on every failed iteration so the empty error message stops hiding the real cause. Investigate why the same client config works for the tray endpoint and not for local-auth.

Fallback UX: if the host gives up, show a "Couldn't reach sidecar" screen with a Retry button rather than dropping the user into the manual-login form.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingopensourceSelf-contained issue suitable for external contributors

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions