Skip to content

[BUG] Update check shows opaque "Invalid HTTP response from GitHub" for all failures (likely unauthenticated API rate limit) #713

Description

@schickling-assistant

Describe the bug

The in-app update check fails with a generic, non-actionable dialog:

Update Check Failed
Unable to check for updates. Please try again later.
Error: Invalid HTTP response from GitHub.

Two things are going on, and I want to separate the definite bug from the likely trigger.

1. The real bug (independent of what triggered it): SimpleUpdater collapses every failure mode into one opaque message.
Any non-success from GitHub — HTTP 403 (rate limit), a network error, or a response-parsing failure — surfaces as the same "Invalid HTTP response from GitHub." string. The user can't tell a transient rate-limit from a real outage, so "try again later" is the only guidance possible. This is actionable regardless of root cause.

2. Likely trigger: the unauthenticated api.github.com rate limit (60 requests/hour per IP).
A release check almost certainly calls api.github.com/repos/altic-dev/FluidVoice/releases/latest unauthenticated. Unauthenticated requests share a per-IP pool of 60/hour; when it's exhausted GitHub returns HTTP 403 with x-ratelimit-remaining: 0. On a developer machine that already talks to the GitHub API for other reasons, that pool is easy to exhaust, which fits the transient "try again later" symptom.

Caveat, in the interest of honesty: I can't confirm the pool was exhausted at the exact moment of the failure. I reproduced a 403 with an exhausted pool from the same machine/IP, but my own debugging requests consumed some of that quota, so I'm presenting rate-limiting as the most likely trigger, not a proven one. The parseGitHubDate / date-formatter code paths mean a response-parsing failure is a theoretical alternative — but the transient "try again later" wording points away from a deterministic parse bug. Either way, item (1) stands.

Reproduction steps

  1. Run FluidVoice on a machine whose IP has exhausted the unauthenticated GitHub API limit (e.g. a dev machine that hits api.github.com), or otherwise force a 403 from api.github.com.
  2. Trigger the update check (automatic on launch, or via the menu).
  3. Observe the failure dialog.

To confirm the rate-limit mechanism directly:

curl -sD - -o /dev/null "https://api.github.com/repos/altic-dev/FluidVoice/releases/latest"
# When limited: HTTP/2 403, x-ratelimit-remaining: 0, body "API rate limit exceeded for <ip>"

Expected behavior

  • Distinguish failure modes and message accordingly. For a 403 with x-ratelimit-* headers, say something like "GitHub rate limit reached, retrying after HH:MM" using x-ratelimit-reset, rather than a generic HTTP error.
  • Reduce the odds of hitting the limit at all:
    • Send If-None-Match (ETag) conditional requests — a 304 Not Modified does not count against the unauthenticated limit.
    • Cache the last successful check and back off; avoid re-checking on every launch/foreground.
    • Respect x-ratelimit-reset for the retry schedule.

Actual behavior

Every update-check failure shows the same "Invalid HTTP response from GitHub." dialog with no way to distinguish a transient rate-limit from a genuine problem, and (if the rate-limit hypothesis holds) no backoff or conditional-request handling to avoid re-hitting the limit.

App Version

1.6.5 (build 16) — note this is already the latest published release (v1.6.5), so a successful check would have reported "already up to date"; instead it errored.

macOS Version

macOS 26.2

Architecture

Apple Silicon

Logs or crash report

No crash. The only surfaced output is the dialog text quoted above ("Error: Invalid HTTP response from GitHub."). Relevant symbols in the binary: SimpleUpdater.checkForUpdate(owner:repo:includePrerelease:), parseGitHubDate, githubDateFormatter, githubFractionalDateFormatter.

Posted on behalf of @schickling
field value
agent_name 🧱 cl1-mesa
agent_session_id a75f30d2-971a-4852-be62-587096f0d663
agent_tool Claude Code
agent_tool_version 2.1.202
agent_runtime Claude Code 2.1.202
agent_model claude-opus-4-8
runtime_profile /nix/store/34v4ljmkjh4sgzax68wwgggmzf69zmfk-coding-agent-runtime-profile/share/coding-agents/profile.json
skills_manifest /nix/store/92pwq9mx1byj8nl9an05dyh4baykvf1d-agent-skills-corpus/share/agent-skills/manifest.json
worktree schickling
machine mbp2025
tooling_profile dotfiles@unknown

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions