Skip to content

fix(updater): send no-cache header on update check to avoid stale manifest#922

Merged
wesbillman merged 1 commit into
mainfrom
brain/fix-updater-cache
Jun 9, 2026
Merged

fix(updater): send no-cache header on update check to avoid stale manifest#922
wesbillman merged 1 commit into
mainfrom
brain/fix-updater-cache

Conversation

@wesbillman

Copy link
Copy Markdown
Collaborator

Problem

"Check for updates" is flaky: the new version is published but the check often reports "up to date" until you click 3–4 times or restart the app.

Root cause (full writeup in RESEARCH/UPDATE_CHECK_FLAKINESS.md): the updater fetches latest.json with a plain GET and no cache directive. The manifest is overwritten in place at a stable URL and served from a multi-node cache (Fastly for OSS, Artifactory HA replicas for the internal build) with no Cache-Control. A check can hit a node still serving the old manifest → version not greater than current → "up to date". A retry/restart eventually lands on a fresh node — the 4-clicks symptom.

Fix

Pass Cache-Control: no-cache request headers on the updater check(), so every poll explicitly revalidates instead of trusting a stale cached copy.

const update = await check({ headers: { "Cache-Control": "no-cache" } });

This sits in runUpdateCheck, the single code path behind both the manual "Check for updates" button and the launch/6h background check, so one edit covers every check.

Scope / honesty

This is app-side hardening — a backstop, not a standalone fix. A shared/origin cache can ignore request headers, so this is most effective paired with the response-side fix landing in squareup/sprout-releases (no-cache on the Artifactory manifest PUT). It meaningfully helps the internal build.

For the public/OSS path to be deterministic, a follow-up is still needed: point the OSS check at a source that isn't asset-CDN-cached (the GitHub API release endpoint returns cache-control: max-age=60 + ETag, bounded staleness, no node roulette). github.com strips the query string before redirecting to the signed asset, so a cache-buster can't beat the OSS asset CDN.

Testing

No hook-test harness exists in this repo yet, and this is a pure pass-through of an options object verified against tauri-plugin-updater's typed API (CheckOptions.headers?: HeadersInit). Full pre-push suite (desktop-test, rust-tests, mobile-test, clippy, tauri-test) passes green. Adding a Tauri-plugin mock + RTL scaffold for a one-liner felt disproportionate — flagging the tradeoff rather than hiding it.

The update check fetched latest.json with a plain GET and no cache directive,
so a check could be served a stale manifest from an intermediate cache and
report "up to date" when a newer version exists. Pass no-cache on the check so
every poll revalidates. Covers both the manual button and the background check
(shared code path).

Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
@wesbillman wesbillman requested a review from a team as a code owner June 9, 2026 16:14
@wesbillman

Copy link
Copy Markdown
Collaborator Author

@codex please review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@wesbillman wesbillman merged commit a357e22 into main Jun 9, 2026
15 checks passed
@wesbillman wesbillman deleted the brain/fix-updater-cache branch June 9, 2026 16:52
tlongwell-block pushed a commit that referenced this pull request Jun 10, 2026
* origin/main:
  Fix post-compact handoff context for OpenAI providers (#931)
  chore(release): release version 0.3.15 (#936)
  fix: persona is source of truth at spawn + thread-depth conventions (#930)
  fix: skip avatar reconciliation for legacy agent records (#933)
  feat(desktop): add nest commit identity guidance with human sign-off (#929)
  feat: provider/model selection for personas and runtime-aware env injection (#794)
  fix: reconcile agent profile on startup when relay publish was missed (#921)
  Revamp first-run onboarding (#924)
  Update setup loading screen (#926)
  fix(dm): keep hidden DMs hidden across refetch via relay-signed visibility snapshot (NIP-DV) (#857)
  Maximize desktop window on launch (#925)
  feat: preview features (experiments settings UI) (#888)
  fix(updater): send no-cache header on update check to avoid stale manifest (#922)
  fix(desktop): refresh channel state after unarchive (#923)
  Add channel visibility & ephemeral TTL controls to manage sidebar (#911)
  ci(release): add Intel macOS (x86_64) DMG as a release target (#748)

Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>

# Conflicts:
#	desktop/src/features/sidebar/ui/AppSidebar.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant