Skip to content

Fix two null-access crashes in onboarding autoconfig and thread-list context menu#2740

Merged
bengotow merged 1 commit into
masterfrom
claude/awesome-ritchie-Irvfk
Jun 13, 2026
Merged

Fix two null-access crashes in onboarding autoconfig and thread-list context menu#2740
bengotow merged 1 commit into
masterfrom
claude/awesome-ritchie-Irvfk

Conversation

@bengotow

@bengotow bengotow commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes two distinct null/undefined crashes surfaced in Sentry for release 1.21.1-ae68e881, together affecting 68 users.


Bug 1 — TryThunderbirdAutoconfig: null autoConfig crashes on .emailProvider access

Sentry issues: MAILSPRING-CLIENT-1G (28 users, Windows) · MAILSPRING-CLIENT-18 (18 users, macOS)

Root cause: getThunderbirdAutoconfig fetches an XML autoconfig URL and returns the result of xml2js.parseStringPromise(body). When the remote server responds with HTTP 200 but an empty or structurally degenerate XML body, parseStringPromise returns null rather than throwing. The function therefore returns null (not false), and the call-site guard:

if (autoConfig !== false && autoConfig.emailProvider) {

passes for nullnull !== false is true — and then autoConfig.emailProvider throws:

Cannot read properties of null (reading 'emailProvider')

This would only happen for users whose email domain has an autoconfig endpoint that returns a 200 with a malformed/empty XML body (e.g. an nginx default page or blank response served at the well-known URL).

Fix: Replace autoConfig !== false with a plain truthy check so both false and null (and any other falsy value) are handled:

if (autoConfig && autoConfig.emailProvider) {

Bug 2 — ThreadListContextMenu.markAsReadItem: undefined thread crashes on .unread access

Sentry issue: MAILSPRING-CLIENT-2V (22 users)

Root cause: menuItemTemplate calls DatabaseStore.modelify(Thread, this.threadIds). modelify builds the result array with arr.map(id => modelsByString[id]) — if an ID is not found in the database (thread was deleted between the user right-clicking and the DB lookup resolving), the map returns undefined for that slot. markAsReadItem (and other methods) then iterates this.threads without null-checking:

const unread = this.threads.every((t) => t.unread === false);

When t is undefined, accessing t.unread throws:

Cannot read properties of undefined (reading 'unread')

Fix: Filter out undefined entries immediately after modelify resolves:

this.threads = threads.filter(Boolean);

This protects all downstream methods (markAsReadItem, markAsSpamItem, archiveItem, etc.) in one place.


Test plan

  • Add an email account whose domain serves a 200 OK but empty body at https://autoconfig.<domain>/mail/config-v1.1.xml — account setup should proceed past autoconfig without crashing
  • Right-click on threads in the thread list; verify no crash when a thread is simultaneously deleted/archived

https://claude.ai/code/session_01JRpspgNR3pz8R2fM8XTZGJ


Generated by Claude Code

… MAILSPRING-CLIENT-18, MAILSPRING-CLIENT-2V)

onboarding-helpers: getThunderbirdAutoconfig returns the raw xml2js parse
result, which is null when the server responds HTTP 200 with an empty or
unparseable XML body. The guard `autoConfig !== false` passed for null,
causing `autoConfig.emailProvider` to throw. Changed to a truthy check so
both false and null are handled correctly.

thread-list-context-menu: DatabaseStore.modelify maps IDs to models but
returns undefined for any ID not found in the database (e.g. a thread that
was deleted between the right-click and the DB lookup). markAsReadItem and
other methods iterated this.threads without null-checking, causing `t.unread`
to throw. Added .filter(Boolean) when assigning this.threads so undefined
entries are dropped before any method touches them.

https://claude.ai/code/session_01JRpspgNR3pz8R2fM8XTZGJ
@indent-staging

indent-staging Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Fixes two null-access crashes reported in Sentry (MAILSPRING-CLIENT-1G, MAILSPRING-CLIENT-18, MAILSPRING-CLIENT-2V) where defensive guards were too narrow. The onboarding Thunderbird autoconfig flow could crash when an HTTP 200 response returned an empty/unparseable XML body (xml2js resolves to null, which the !== false guard let through), and the thread-list context menu could crash when a thread was deleted between right-click and the DB lookup (DatabaseStore.modelify returns undefined for missing IDs).

  • onboarding-helpers.ts: Widen the Thunderbird autoconfig guard from autoConfig !== false to a truthy check so null returns from getThunderbirdAutoconfig are handled alongside the existing false error path.
  • thread-list-context-menu.ts: Apply .filter(Boolean) to the DatabaseStore.modelify result before assigning this.threads, so downstream methods (markAsReadItem, starItem, archiveItem, etc.) never see undefined entries for deleted threads.

Issues

No issues found.

CI Checks

All CI checks passed for commit 80d36ef.

Custom Rules 3 rules evaluated, 3 passed, 0 failed

Passing This is a longer title to see what happens when they are too long to fit
Passing B
Passing Ben Rule

View all rules

@bengotow bengotow merged commit 3b50544 into master Jun 13, 2026
2 checks passed
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.

2 participants