Skip to content

fix(notifications): guard UN calls when running unbundled (fixes tauri dev crash) - #38

Merged
AzimovS merged 1 commit into
mainfrom
fix/notifications-unbundled-dev-crash
Apr 21, 2026
Merged

fix(notifications): guard UN calls when running unbundled (fixes tauri dev crash)#38
AzimovS merged 1 commit into
mainfrom
fix/notifications-unbundled-dev-crash

Conversation

@AzimovS

@AzimovS AzimovS commented Apr 21, 2026

Copy link
Copy Markdown
Owner

Summary

pnpm run tauri dev / ./clean_run.sh crashes at startup on macOS with:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'bundleProxyForCurrentProcess is nil:
mainBundle.bundleURL file:///.../target/debug/'

Commits #32 / #33 migrated macOS notifications from tauri-plugin-notification (legacy NSUserNotification, which delivers on unbundled binaries but no longer fires banners on modern macOS) to UNUserNotificationCenter. The modern UN API dereferences NSBundle.mainBundle and throws when the executable was launched directly rather than from a .app bundle. tauri dev runs target/debug/meetily (not the bundled .app), so UN crashes the app before the window even opens.

Fix

Add an is_running_in_app_bundle() guard in frontend/src-tauri/src/notifications/macos_un.rs that checks whether current_exe() has a *.app ancestor, and early-return from the two UN entry points when unbundled.

Two subtleties worth calling out:

  1. request_authorization returns Err, not Ok(false). manager.rs:60-62 persists the result via set_system_permission(granted). Dev runs and the bundled .app share the same config directory (~/Library/Application Support/com.meetily.ai/notification_settings.json), so returning Ok(false) would write system_permission_granted = false to disk and silently suppress every real notification in the shipped app until the user manually re-granted consent. Returning Err hits the existing Err arm at manager.rs:69-72 which logs and continues without persisting — no cross-build poisoning.
  2. show() returns Ok(()). Callers propagate the Result upward but do not store state on success, so a silent no-op is safe in dev.

Bundled .app behavior is unchanged — the guard only fires when the ancestor walk finds no .app.

Test plan

  • cargo check clean
  • ./clean_run.sh (= pnpm run tauri dev) on macOS: app starts; logs show UN authorization skipped: not running inside a .app bundle (likely \tauri dev`)followed byNotification system initialized successfully`. No crash.
  • Build the .app (pnpm run tauri:build or ./clean_build.sh) and launch it directly → first-run macOS permission prompt appears; test notifications fire as banners.
  • Verify system_permission_granted in ~/Library/Application Support/com.meetily.ai/notification_settings.json is not overwritten by a tauri dev run.

Known follow-ups (intentionally out of scope)

  • Lifting the capability check into system.rs so macos_un.rs stays a pure UN transport.
  • A dev fallback to tauri-plugin-notification so devs get some banner delivery during tauri dev (at the cost of keeping the legacy dep on macOS).
  • Replacing path-sniffing with NSBundle.mainBundle.bundleIdentifier().is_some() for a more authoritative signal.

`UNUserNotificationCenter` dereferences `NSBundle.mainBundle` and throws
`NSInternalInconsistencyException: bundleProxyForCurrentProcess is nil`
when the executable is launched directly (e.g. `pnpm run tauri dev`,
which runs `target/debug/meetily` rather than the bundled `.app`). Since
commits #32/#33 migrated macOS notifications from the legacy
`NSUserNotification` path to UN, every `tauri dev` run crashes at
startup.

Skip the UN calls when `current_exe()` has no `.app` ancestor.
`request_authorization` returns `Err` (not `Ok(false)`) so
`manager.rs` falls into its existing `Err` arm and does not persist
`system_permission_granted = false` — the dev run and the bundled `.app`
share `~/Library/Application Support/com.meetily.ai/`, so persisting
`false` would silently suppress every real notification until the user
re-granted consent. `show()` returns `Ok(())`; callers do not store
state on success.

Bundled `.app` behavior is unchanged.
@AzimovS
AzimovS merged commit 4272042 into main Apr 21, 2026
@AzimovS AzimovS mentioned this pull request Apr 24, 2026
6 tasks
AzimovS added a commit that referenced this pull request Apr 24, 2026
Prepares a release containing the work merged since v0.1.15:
- feat(detection): mic-activity meeting auto-detection on macOS (#35)
- feat(transcription): retry with backoff + in-transcript failure
  placeholder (#39)
- feat(summary): TownHall template (#30), current template name in
  dropdown (#31), specificity prompt tweak (#37)
- feat(remote): test-connection button + model selection surface
  improvements (#28, #29)
- fix(notifications): migrate to UNUserNotificationCenter (#32),
  SAFETY + fallback race fixes (#33), unbundled-dev crash guard (#38),
  drop OS recording banners and flip auto_save default to false (#36)
- chore(about): drop Zackriya services CTA (#40)

Behavior change to call out in release notes: fresh installs (and
users with no stored recording_preferences.json) now default
auto_save to false — audio files are not written to disk unless the
user opts in via Recording Settings. Existing users with saved
preferences are unaffected.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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