feat(engine): write last-panic.log + flush Sentry on panic in the CLI#3871
Merged
Conversation
9606fef to
6fdc52a
Compare
The screenpipe CLI/engine binary had no panic hook: integrators who embed it as a child process (e.g. inside an Electron wrapper) only saw an exit code when it died, never why. The last-panic.log + Sentry-flush machinery existed only in the Tauri desktop app. Port it to the engine binary on the Record (server) path: - rotate last-panic.log -> .prev on startup, then append the panic message + backtrace (fsync'd) so the embedding parent can read the cause after the process exits - flush Sentry in the hook so a fast-exiting panic isn't dropped - written regardless of telemetry, so analytics-disabled customers still get a local crash record - written to the resolved data dir (honors --data-dir) so it sits next to screenpipe.log and doesn't collide with the desktop app's ~/.screenpipe/last-panic.log - stamp the record with the existing embedder attribution (TelemetryContext: SCREENPIPE_EMBEDDER / SCREENPIPE_CUSTOMER_ID / ...) so it's identifiable with telemetry off; the Sentry scope already carries the same tags Scope: the binary/child-process embedding only. Covers Rust panics; native crashes (DLL access violation, SIGSEGV), Defender kills, and OOM are not panics (need exit-code mapping / a native handler). The in-process @screenpipe/sdk (napi addon over screenpipe-recorder) doesn't run main(), so it's unaffected and needs panic handling at the napi boundary instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6fdc52a to
8e76c4f
Compare
louis030195
pushed a commit
that referenced
this pull request
Jun 6, 2026
Ships the merged Windows fixes (#3870: sign CLI binary, fix auto-destruct screenpipe-app.exe false-kill, hard-fail VC++ DLL bundling; #3871: last-panic.log + Sentry flush on panic) plus the new work-hours schedule CLI flags (--schedule-enabled / --schedule-rule). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
what
The
screenpipeCLI/engine binary had no panic hook. Integrators who embed it as a child process (for example inside an Electron wrapper) only saw an exit code when it died, never why. Thelast-panic.log+ Sentry-flush machinery existed only in the Tauri desktop app (apps/screenpipe-app-tauri/src-tauri/src/main.rs).This ports it to the engine binary, installed on the
Record(long-running server) path only. Subcommands return earlier, so they are unaffected.how it behaves on a panic
flowchart TD A["panic on any thread"] --> B{"tokio shutdown-time noise?"} B -- yes --> Z["suppress, stderr only"] B -- no --> C["capture payload + location + backtrace"] C --> D["append to last-panic.log in the resolved data dir (fsync); rotated to .prev on startup"] C --> E["print PANIC to stderr"] C --> F["Sentry capture_message Fatal + flush 2s (no-op if telemetry off)"] D --> G["embedding parent reads last-panic.log after the child exits"]what is covered
does it conflict with the desktop app?
No. The desktop app runs its engine in-process (
ServerCore::start) and has its own panic hook; this hook only runs when thescreenpipebinary is executed directly, so they are different processes. The only shared resource would be thelast-panic.logfile, so the hook writes to the resolved data dir (honors--data-dir) rather than always~/.screenpipe. An embedder running with its own--data-dirtherefore keeps its crash log next to its ownscreenpipe.logand never touches the app's file. (The app does not readlast-panic.logon startup, so even a shared dir would not cause misattribution, only rotation churn.)what about the SDK?
Out of scope here, on purpose.
@screenpipe/sdkis an in-process napi addon over thescreenpipe-recordercrate; it never runs this binary'smain(), so it gets nothing from this hook and is also not destabilized by it. A library should not install a globalstd::panic::set_hook; crash handling for the SDK belongs at the napi boundary (translate a Rust panic into a JS error the host handles) and is a separate change in that crate.attribution
No new env var. The crash record reuses the existing
TelemetryContextembedder attribution (SCREENPIPE_EMBEDDER/SCREENPIPE_HOST_APP/SCREENPIPE_CUSTOMER_ID/SCREENPIPE_DEPLOYMENT_ID), so a local crash record is identifiable even with telemetry off. When telemetry is on, the Sentry scope is already tagged with the same context (see theconfigure_scopeblock just above the hook), so panic events inherit it.sample
last-panic.log(The attribution line is omitted when no
SCREENPIPE_*context vars are set.)notes / follow-ups
sample_rateis0.1, solast-panic.logis the reliable record while the Sentry copy is best-effort. If we want guaranteed panic delivery to Sentry, bump the rate or special-case fatal events.test plan
cargo check -p screenpipe-engine --bin screenpipecompiles, no new warningscargo test -p screenpipe-engine --lib crash_logpasses (4 tests: append, create-missing-dir, rotate, rotate-no-op). Thecrash_loghelpers take the dir as an argument, so they are agnostic to the data-dir change.rustfmt --checkclean on the changed files🤖 Generated with Claude Code