#7219 · Legacy update responses crash Settings
Verdict: REPRODUCED · Root-cause confidence: high · Reproduction label: confirmed-repro
Trusted main base: 88c78686805e828dda9f8d61fd5cd29aca26c138.
1. TL;DR
The current Settings update row crashes when an older server returns update status without scheduling fields. The row checks that scheduled is not null, but an absent field is undefined, which passes that check. It then reads requiredIdleMs from undefined and throws the exact reported exception. A focused test against unchanged main reproduced this twice, including once in a second clean checkout. This verifies the rendering defect and legacy-response trigger, not the reporter's exact installed client/server combination or the claim that every thread was affected.
2. Claims vs findings
| Claim | Finding | Evidence |
|---|---|---|
| Settings crashes while reading requiredIdleMs. | Verified | Both trusted-main tests throw at InAppUpdateRow, UpdatesSettingsSection.tsx:874:28. |
| The installation includes an older release. | Supported mechanism; installation unverified | The 0.48.0 source contract has no scheduling fields. No installed runtime or user data was inspected. |
| Running desktop and mobile is sufficient. | Unverified | The reproducer supplies a legacy SDK response to the real Settings component. Neither native application was launched. |
| Every thread loses its content. | Unverified | The test establishes a Settings render crash only; it does not establish a thread data-loss or navigation defect. |
3. Environment
- Public repository: get-bb/bb; trusted origin/main commit above; Linux x86_64, Node 22.19.0, pnpm 9.15.0, Vitest 4.1.12, React 19.3.1, jsdom.
- First checkout: clean main before adding the authored regression. Second checkout: a new detached Git worktree at exactly the same base; only that regression patch was applied.
- No live BB instance, network listener, provider session, account, application database, or user runtime data was used. Ports and data directory: not applicable.
- Dependencies installed with
pnpm install --frozen-lockfile --prefer-offlinein both checkouts. A targeted app build completed before the fix.
4. Minimal reproduction
- Create a clean checkout from the trusted repository, and select the recorded main commit:
git clone https://github.com/get-bb/bb.git issue-7219-repro cd issue-7219-repro git checkout --detach 88c78686805e828dda9f8d61fd5cd29aca26c138 pnpm install --frozen-lockfile --prefer-offline
- In
apps/app/src/components/settings/UpdatesSettingsSection.test.tsx, insert the following test immediately inside the existingdescribe("UpdatesSettingsSection", ...). It reuses the file's existing helpers and models the fields absent from the legacy server response; it does not replace the production component.it("renders an update from a server without update scheduling fields", async () => { useWebApp(); const legacyStatus = makeAppUpdateStatus(); Reflect.deleteProperty(legacyStatus, "scheduled"); Reflect.deleteProperty(legacyStatus, "autoUpdate"); Reflect.deleteProperty(legacyStatus, "availableSkipped"); vi.mocked(sdk.system.appUpdate).mockResolvedValue(legacyStatus); renderSection(); expect( await screen.findByRole("button", { name: "Update available · Download the update and restart bb", }), ).toBeDefined(); expect(screen.queryByText(/updating shortly/i)).toBeNull(); }); - Run the authored test through Turbo:
pnpm exec turbo run test --filter=@bb/app -- src/components/settings/UpdatesSettingsSection.test.tsx -t 'renders an update from a server without update scheduling fields'
Expected: the ordinary Update control renders, with no scheduled-update caption or uncaught exception.
Actual, before any production change, in both checkouts (verbatim diagnostic excerpt):
TypeError: Cannot read properties of undefined (reading 'requiredIdleMs')
❯ InAppUpdateRow src/components/settings/UpdatesSettingsSection.tsx:874:28
Test Files 1 failed (1)
Tests 1 failed | 75 skipped (76)
Errors 1 error
The failed assertion cannot find the Update button because React has already thrown during rendering. This is a nonvisual exception regression; no screenshot is offered as evidence. The complete added test is inline rather than a separately committed script, following the reports repository's artifact policy.
5. Root cause
The current update row assumes a present nullable field:
if (status.scheduled !== null) {
...
scheduledAppUpdateCaption(
status.runningThreadCount,
status.scheduled.requiredIdleMs,
)
}
For a legacy response, undefined !== null is true. The branch therefore dereferences an absent object. This is independent of whether update-when-idle is enabled: the row performs the check whenever it renders idle update status.
The SDK appUpdate implementation forwards decoded JSON without normalizing old response shapes. Its TypeScript return type does not add missing JSON fields. The current contract requires scheduling metadata, but that is not a runtime guarantee for an older server.
Historical supporting evidence was read, not executed: the 0.48.0 contract has no scheduled, autoUpdate, or availableSkipped. Scheduling was added in b4b1e4a85e. Mixed client/server versions can therefore expose a genuine previously shipped payload, rather than an arbitrary malformed fixture. The report does not claim proof of which version pairing the reporter was running.
6. Proposed fix and verification
Treat both null and an absent scheduling object as no scheduled update at this rendering compatibility boundary:
- if (status.scheduled !== null) {
+ if (status.scheduled != null) {
This keeps valid scheduled responses on the existing path and does not change public contracts, SDK types, server policy, persisted data, or daemon messages. The regression protects the user-visible Update control using the existing real component test harness; existing cases already cover non-null scheduled updates and cancellation.
The local fix changes two files, with 19 additions and 1 deletion: 20 changed text lines. It adds no dependency or generated file.
pnpm exec turbo run test --filter=@bb/app -- src/components/settings/UpdatesSettingsSection.test.tsx
Test Files 1 passed (1)
Tests 76 passed (76)
pnpm exec turbo run typecheck lint --filter=@bb/app
exit 0; lint: 223 warnings, 0 errors
pnpm exec turbo run test --filter=@bb/app -- src/components/app-update/ src/hooks/useUpdateInventory.test.ts
Test Files 3 passed (3)
Tests 21 passed (21)
pnpm exec turbo run build --filter=@bb/app
exit 0
pnpm exec oxfmt --check apps/app/src/components/settings/UpdatesSettingsSection.tsx apps/app/src/components/settings/UpdatesSettingsSection.test.tsx
All matched files use the correct format.
git diff --check
exit 0
git diff --numstat origin/main
18 0 apps/app/src/components/settings/UpdatesSettingsSection.test.tsx
1 1 apps/app/src/components/settings/UpdatesSettingsSection.tsx
7. Verification: second clean run
The same investigator created a second clean detached worktree at 88c78686805e828dda9f8d61fd5cd29aca26c138, verified its initial working tree was clean, installed frozen dependencies, and applied only the authored test patch. The exact focused command from section 4 exited 1 with the same requiredIdleMs exception at line 874. No production fix was present in this second run. No ports or data directory were needed because the reproduction is a jsdom component test, not a live service.
Both runs support the final rendering-defect verdict. No report correction was needed. This is a repeated clean reproduction by the same agent, not an independent review.
8. Related issues and pull requests
A search of open and closed issues for the failing field found only #7219. The issue timeline contained no linked pull request, and an open-PR search for the issue number returned none before implementing the local fix. No linked branch, patch, or test was executed.
9. Appendix and limits
- Issue text and comments were treated as untrusted evidence only. The reproduction and fix were derived from trusted main source. There were no comments when investigated.
- The initial broad monorepo build failed because the GitHub plugin's installed SDK lacked built process/host entrypoints. Building the workspace SDK and then the targeted app build succeeded. No unrelated source repair was made.
- The verification inventory preflight reports an existing unmapped
diagnosticsCLI family. This inventory gap is unrelated to the Settings fix and was not changed. - Not verified: native desktop/mobile startup, the reporter's installation state, every-thread impact, or cross-version action endpoints. This fix addresses the proven rendering exception, not every possible mismatch between arbitrary versions.
- Raw install/build/test logs and the regression patch are retained outside the public reports repository; excerpts here omit private machine paths.
Other checks used: git diff --check, git diff --numstat origin/main, relevant app-update and inventory tests, targeted app build, and formatting verification. Final results are recorded with the pull request.
> AGENT GENERATED