Browse docs
Godot
The official sentry-godot GDExtension reports crashes, GDScript errors, and breadcrumbs to GlitchReplay with no protocol changes — just point the DSN at us.
GlitchReplay speaks the Sentry envelope protocol, so the official sentry-godot GDExtension works against your project with one change: the DSN host. You get GDScript stack traces, native crash capture, breadcrumbs, optional screenshots, and scene-tree attachments.
1. Install the addon
Download the latest release zip from github.com/getsentry/sentry-godot/releases and extract the addons/sentry/ directory into your project, preserving the exact casing. The download bundles the GDExtension binaries for Windows, macOS, Linux, iOS, Android, and Web — Godot will pick the right one per export target.
Heads up — the release zip is ~390 MB because it ships every-platform binaries plus dSYMs. If you only target desktop on one OS for development, you can prune the addon to ~20 MB before checking it in. For example, a macOS-only dev workflow keeps just the release dylibs:
# After extracting addons/sentry/ into your project:
cd addons/sentry/bin
rm -rf ios linux windows android web noop # other-platform binaries
rm -rf macos/dSYMs # debug symbols (~80 MB)
rm -f macos/libsentry.macos.debug.dylib # debug dylib (~80 MB)If you remove the macOS debug dylib, also point macos.debug at the release lib in addons/sentry/sentry.gdextension so Godot debug builds still load:
[libraries]
macos.debug = "res://addons/sentry/bin/macos/libsentry.macos.release.dylib"
macos.release = "res://addons/sentry/bin/macos/libsentry.macos.release.dylib"Re-extract the omitted binaries when you add export targets. Symbolicated stack traces from the SDK itself (rare for game devs) need the original libsentry.macos.debug.dylib + dSYMs back in place.
Restart the Godot editor after extracting so the GDExtension is registered and the Sentry section appears in Project Settings. On first open, the editor populates .godot/extension_list.cfg — if you skipped opening the editor and went straight to a --headless smoke test, add res://addons/sentry/sentry.gdextension to that file by hand so the extension actually loads.
2. Required: enable GDScript call stacks
sentry-godot refuses to start without debug/settings/gdscript/always_track_call_stacks. Without it you'll see this on first run and no events will be sent:
ERROR: Sentry: Please enable `debug/settings/gdscript/always_track_call_stacks` in your Project Settings. This is required for supporting script stack traces.Toggle it on in Project Settings → Debug → Settings → Gdscript → Always Track Call Stacks, or add this block to project.godot:
[debug]
settings/gdscript/always_track_call_stacks=trueGodot 4.5+ is required (the GDExtension's compatibility_minimum). Older projects need to upgrade before the SDK loads.
3. Configure the DSN
In the Godot editor, open Project → Project Settings…, switch to the Sentry section, and paste your GlitchReplay DSN under Options → Dsn. The DSN comes from Project → Settings → Data Source Name in the GlitchReplay dashboard and looks like https://<public_key>@glitchreplay.com/<project_id>.
Settings are persisted to project.godot. If you prefer to check the file in by hand, add a section like this:
[sentry]
options/dsn="https://<public_key>@glitchreplay.com/<project_id>"
options/environment="dev"
options/release="0.1.0"
options/sample_rate=1.0
options/max_breadcrumbs=100
options/send_default_pii=false
options/attach_screenshot=false
options/attach_scene_tree=false
options/enable_logs=falseThe keys you'll most often touch:
- dsn — point at
glitchreplay.com, notsentry.io. Easy to miss when copying configs. - environment —
dev,staging,prod. Filterable in the GlitchReplay dashboard. - release — leave blank to auto-derive from app name + version, or set a git short hash so first-seen-in-release alerts and source maps line up.
- attach_screenshot — captures a viewport PNG when an error fires. Off by default; turn on for visual bug repros.
- attach_scene_tree — serializes the active scene tree alongside the event. Useful for "what was on screen" diagnostics.
- send_default_pii — leave
falsefor games. Player IPs and OS usernames are usually not what you want stored. - max_breadcrumbs — defaults to 100. Plan your forwarder (next section) to fit under this cap.
4. Verify with a smoke test
Drop a one-off node into your debug scene to confirm events are arriving:
extends Node
func _ready() -> void:
SentrySDK.add_breadcrumb(SentryBreadcrumb.create("about to greet"))
SentrySDK.capture_message("Hello from Godot", "info")Run the project, then open the GlitchReplay dashboard. You should see a new Hello from Godot issue within a few seconds. To verify unhandled-error capture, throw a deliberate assert:
@warning_ignore("assert_always_false")
assert(false, "deliberate test error from debug menu")The SDK hooks Godot's logger, so any push_error, runtime exception, or failed assert is captured automatically — no try/ catch, no manual capture_exception call required.
Tip: for an iterative dev loop, wire the two calls above into your debug menu (F9 in most templates) instead of dropping a throwaway node. You get on-demand event triggers without code changes whenever you tweak DSN or sample rates:
# scenes/ui/debug_menu.gd — inside _ready() near other test buttons
var btn_msg := Button.new()
btn_msg.text = "Send test message"
btn_msg.pressed.connect(func():
SentrySDK.capture_message("Smoke test @ %s" % Time.get_datetime_string_from_system(), "info"))
sentry_row.add_child(btn_msg)
var btn_err := Button.new()
btn_err.text = "Throw test error"
btn_err.pressed.connect(func():
@warning_ignore("assert_always_false")
assert(false, "deliberate test error from debug menu"))
sentry_row.add_child(btn_err)5. Recommended: a CrashReporter autoload
For game projects, the most useful thing you can do beyond the default capture is forward a small set of high-signal gameplay events as breadcrumbs. The trap to avoid: per-frame or per-unit events fill the 100-crumb buffer in seconds and push out the lifecycle context that would actually help debug a crash.
Pick events that happen tens of times per session, not thousands: lifecycle (game started, paused, over), match outcomes, AI command-chain decapitations, objective state changes, operator-issued abilities. Skip projectile-fired, unit-killed, per-frame-tick.
# scripts/autoload/crash_reporter.gd
extends Node
## Forwards high-signal gameplay events to Sentry as breadcrumbs/tags so any
## GDScript error or native crash arrives at GlitchReplay with the recent
## battle context attached.
const _SDK_NAME := "SentrySDK"
var _sdk: Object = null
var _enabled: bool = false
func _ready() -> void:
# Probe for the GDExtension class first so the autoload still loads
# cleanly on platforms where the binary isn't shipped (e.g. headless CI).
if ClassDB.class_exists(_SDK_NAME):
_sdk = Engine.get_singleton(_SDK_NAME) if Engine.has_singleton(_SDK_NAME) else null
_enabled = _sdk != null
if not _enabled:
push_warning("[CrashReporter] SentrySDK not available — breadcrumbs disabled")
return
_set_static_tags()
_wire_breadcrumbs()
func _set_static_tags() -> void:
_set_tag("godot_version", Engine.get_version_info().get("string", "unknown"))
_set_tag("os", OS.get_name())
_set_tag("build", _git_short_hash())
func _wire_breadcrumbs() -> void:
GameEvents.game_started.connect(func(): _crumb("game_started", "lifecycle"))
GameEvents.game_over.connect(func(won: bool):
_crumb("game_over won=%s" % str(won), "lifecycle"))
GameEvents.objective_state_changed.connect(func(_o, old_s: int, new_s: int):
_crumb("objective %d -> %d" % [old_s, new_s], "objective"))
# ... add any other low-frequency, high-signal events here.
# ── Public API ─────────────────────────────────────────────
func report_message(msg: String, level: String = "info") -> void:
if _enabled and _sdk.has_method("capture_message"):
_sdk.call("capture_message", msg, level)
func set_battle_tags(scenario: String, side: String) -> void:
_set_tag("scenario", scenario)
_set_tag("player_side", side)
# ── Internals ──────────────────────────────────────────────
func _crumb(message: String, category: String, level: String = "info") -> void:
if _enabled and _sdk.has_method("add_breadcrumb"):
_sdk.call("add_breadcrumb", message, category, level)
func _set_tag(key: String, value: String) -> void:
if _enabled and _sdk.has_method("set_tag"):
_sdk.call("set_tag", key, value)
func _git_short_hash() -> String:
var output: Array = []
var exit := OS.execute("git", ["rev-parse", "--short", "HEAD"], output, true)
if exit == 0 and not output.is_empty():
return String(output[0]).strip_edges()
return "unknown"Register it in project.godot at the top of the autoload list:
[autoload]
GameEvents="*res://scripts/autoload/game_events.gd"
CrashReporter="*res://scripts/autoload/crash_reporter.gd"With this in place, any unhandled error arrives with the last ~100 meaningful gameplay events attached — far better than a bare stack trace.
6. Source maps and releases
Set options/release to a stable identifier (a git short hash works) so the dashboard groups events by build. GlitchReplay's source maps page covers symbolicated stack traces; for Godot, GDScript line numbers are already readable, but if you ship native modules, upload their debug symbols there.
Common gotchas
- Wrong DSN host — the addon README points at
sentry.io. Replace withglitchreplay.cominproject.godot. - Headless / CI runs — the GDExtension binary isn't loaded in
--headlessmode on every platform. ProbeClassDB.class_exists("SentrySDK")before using the SDK so your tests boot cleanly. - Breadcrumb spam — wiring per-frame or per-unit signals fills the 100-crumb buffer in seconds. Forward lifecycle and rare-event signals only.
- Web export & CSP — if you self-host the wasm build behind a strict
connect-src, addhttps://glitchreplay.comso envelopes can leave. - Asset library version — install from GitHub Releases, not the in-editor Asset Library. The Asset Library copy lags behind and may be missing the GDExtension binary for your target.
- Parse-error noise during
--headless --quit— with the SDK loaded, a smoke run that immediately quits sometimes logs Identifier "X" not declared for autoloads that work fine in interactive runs. The SDK's GDScript logger hook fires before the autoload symbol table is fully built. Non-fatal — events still flow, and the editor opens cleanly. Verify your interactive F5/F6 boots cleanly before chasing it. - Pre-existing crashes suddenly visible — sentry-godot installs a SIGSEGV/NSException signal handler. Bugs that previously showed as a benign
A Thread object is being destroyed without its completion having been realizedwarning may now generate full crash reports during shutdown. The SDK isn't causing them — it's exposing them. Track them as separate bugs. - macOS Gatekeeper / quarantine — if you copy the downloaded dylibs across machines and macOS attaches
com.apple.quarantine, dlopen will fail silently. Strip withxattr -dr com.apple.quarantine addons/sentry/bin/macos.
Next: configure alerts so a deliberate test crash actually pages you, or read the SDK overview for non-Godot setups.