Electron SDK
Crashpad minidump crash reporting for Electron apps (main + renderer) via @botiverse/hands-electron.
@botiverse/hands-electron adds crash reporting to Electron apps. Electron's built-in Crashpad captures native minidumps for both the main and renderer processes and uploads them directly to Hands, where they become crash tickets and are symbolicated server-side against your uploaded Breakpad symbols.
Like @sentry/electron, the SDK has two entry points — one imported in the main process, one in the renderer.
Install
npm install @botiverse/hands-electron
electron is a peer dependency provided by your app.
Main process
Call init() once, before the app is ready:
import { app } from "electron";
import * as Hands from "@botiverse/hands-electron/main";
Hands.init({
appSlug: "my-desktop-app", // your Hands app slug
clientKey: "qk_live_...", // public client key (safe to ship)
release: app.getVersion(), // version_name; defaults to app.getVersion()
versionCode: 1020300, // Hands version_code → selects the symbol set
environment: "stable", // channel
extra: { deployment: "ga" }, // static annotations on every crash
onCrash: (info) => {
// renderer / child-process termination, including oom / killed exits that
// produce no minidump — log it, show a recovery dialog, etc.
console.warn("process gone:", info.processType, info.reason);
},
});
Attach context that rides along on the next crash:
Hands.setUser({ id: "u_123" });
Hands.setTag("feature", "editor");
Hands.setExtra("open_docs", 3);
Hands.addBreadcrumb({ message: "opened project", category: "ui" });
The main entry starts Crashpad, listens for render-process-gone and child-process-gone, and receives scope forwarded from renderers.
Renderer process
Renderer crashes are captured by the main-process Crashpad automatically. The renderer entry only manages scope and forwards it to main over IPC:
import * as Hands from "@botiverse/hands-electron/renderer";
Hands.setTag("route", location.pathname);
Hands.addBreadcrumb({ message: "clicked export" });
For sandboxed renderers (contextIsolation: true), expose the API from your preload script instead:
// preload.ts
import { exposeHands } from "@botiverse/hands-electron/preload";
exposeHands(); // → window.hands.setTag(...), window.hands.addBreadcrumb(...)
Symbols
Minidumps become readable stacks only when Hands has your app's Breakpad symbols for that version_code. In CI:
- Generate
.symfiles withdump_symsfor your app and the Electron framework binaries. - Zip them (a flat zip is fine — Hands reads each file's
MODULEheader to place it in the Breakpad tree). - Upload alongside the release:
hands builds publish-electron my-desktop-app \
--version-name 1.2.3 --version-code 1020300 \
--installer dist/MyApp-1.2.3.exe \
--symbols symbols.zip
Each crash ticket then gets a symbolicated stack posted as a comment. Without symbols, Hands still records the crash with module+offset frames and leaves a tip to upload them.
What gets sent
Every minidump carries a Sentry-style annotation set the server folds into the ticket: product_type=electron, version, version_code, environment/channel, platform, arch, process_type, electron_version, chrome_version, plus any extra and the current user / tags / breadcrumbs.
How it reaches Hands
The SDK points Electron's crashReporter.submitURL at POST /public/v2/apps/<slug>/minidump?client_key=<key>. Crashpad POSTs the minidump as upload_file_minidump with the annotations as form fields; Hands stores the dump as a crash-ticket attachment and runs the symbolication lane. No update-check or feedback wiring is required for crash reporting.