↓Skip to main content
  1. Talks/

Crashes Don't Care What Language You Wrote Your Game In

·
2026 Godotfest26 Talk

A Godot game is rarely written in just one language. You’ve got GDScript for gameplay, more and more C#/.NET for the heavier systems, and native C++ through GDExtension underneath. Then you export the whole thing to Windows, Linux, macOS, Android, iOS, the Web, SteamOS/Proton, and - for some of us - consoles.

Every one of those languages fails differently. Every one of those platforms reports and symbolicates crashes differently. So when a player hits a bug you can’t reproduce, the problem is collapsing that entire grid of languages and platforms into one readable issue, no matter what threw it or where it died.

This talk walks through that problem end-to-end, using Sentry’s Godot SDK - recently expanded with first-class .NET support - as the example. We’ll look at how a GDScript error, a C# exception, and a native crash differ; how the same crash has to survive Android tombstones, WASM in the browser, and the Proton translation layer on SteamOS; and how you can still get all of it back as a single, grouped, source-mapped issue. It’s a talk about the messy reality of shipping a Godot game everywhere at once - and how to stay sane while doing it.

You write in three languages and ship to eight platforms. A crash can come from any cell in that grid. You need to turn that grid back into one clear answer.

Most crash-reporting talks assume one language and one platform. Godot oftenbreaks both assumptions at the same time, which is exactly what makes it relevant for all Godot developers,whether they’re all-in on GDScript, betting on C#, or dropping into native code for performance.

Axis 1 - The languages: three ways a game can fail
#

Under one engine, “an error” means three different things:

  • GDScript - Errors come from the engine’s own script VM. You want the script, the line, and the game state around it, captured live as the game runs.
  • C#/.NET - The newest and fastest-growing surface. These are managed-runtime exceptions with their own stack shape, and the SDK now captures them as first-class events (including script context and variable values on the platforms that support it). Godot’s C# community has been waiting on solid tooling here, which is a big part of why this talk is timely. There’s even a build-time check that nudges C# developers toward the Godot-aware SDK instead of the raw one, a small thing that prevents a lot of misconfigured setups.
  • Native / GDExtension (C++) - When your own native module or the engine itself crashes, you don’t get an exception at all; you get a full-process minidump from an out-of-process handler. A completely different capture model from the two above. The point for the audience: these aren’t three flavors of the same thing. They fail differently, they carry different context, and unifying them means agreeing on what an event even is before you can put them together in one list.

Axis 2 - The platforms: the same crash, eight hostile environments
#

Now take any of those failures and ship it across Godot’s export targets. Each one has different rules:

  • Desktop (Windows / Linux / macOS) - the baseline, and even here you’re juggling different symbol formats and, on Apple, a separate underlying SDK.
  • Android - its own crash world: tombstones, ANRs (“Application Not Responding”), app hangs, and multiple CPU architectures to build and symbolicate for.
  • iOS - managed through the platform’s native tooling, with its own signing and symbol quirks.
  • Web (WASM) - your game is now running in a browser sandbox; a crash looks nothing like it does on desktop.
  • SteamOS / Proton - your Windows build running through a translation layer on Linux. When it crashes, which stack are you even looking at? One example: under Wine the thread’s stack bounds can report back wrong, so the crash handler tries to capture a wildly oversized stack - the SDK has to detect that it’s running under Proton and rein the capture back in.
  • Consoles - locked-down platforms where you can’t just drop in a standard crash handler at all. This axis is where a lot of the complexity lies and it’s the part many devs underestimate until they ship. Turning the grid back into one answer

The payoff of the talk is how the two axes come back together:
#

  • One SDK, many engines underneath. The Godot SDK is built as a GDExtension that quietly composes several native SDKs (the native/C++ crash backend, plus the Apple, Android, and Web SDKs) and presents a single, clean GDScript and C# API on top. You write against one interface; it fans out to the right machinery per platform.
  • Normalize, then symbolicate. Events from three different runtimes get normalized into one shape, and then each is mapped back to your source - GDScript line, C# frame, or native symbol - using the right method for that platform.
  • One list, grouped and readable. A C# exception under Proton and a native crash on Android end up side by side as grouped, source-mapped issues - plus Godot-native context like the scene tree, a screenshot of what the player saw, breadcrumbs, and device info.
  • Where it’s heading. Once every language and platform lands as structured, richly-contextual data, that context becomes the raw material for AI-assisted root cause - a natural, non-salesy closing note.

Under the hood: how the layers actually talk (and where it got messy)
#

This is the technical spine I’d want to give the talk real weight. Focus on the handful of design decisions and hard-won bugs that make the “one clean issue” illusion hold together. It’s the part senior devs in the room will recognize from their own native/managed bridges.

  • One interface, swappable backends. Everything the public API does - set a tag, add a breadcrumb, capture an event - goes through a single internal C++ interface. Desktop fulfills it with the native/crashpad backend; Apple, Android, and Web each plug a different underlying SDK in behind the exact same interface. The GDScript or C# you write never knows, or cares, which one answered underneath.
  • The C#↔native bridge is hand-built, and it’s mostly about strings and structs. Godot’s C# layer doesn’t get to use a normal SDK. The managed and native sides talk over a raw function-pointer boundary. Native registers a table of callbacks C# can invoke; C# imports a matching set of native entry points. Every string crosses as a raw UTF-16 pointer + length with explicit handoff, and every struct has to have byte-for-byte identical layout on both sides. Get one field out of order and there’s no compiler to catch it - you get a crash while reporting a crash.
  • Keeping scope in sync without an infinite loop. Set the user, a tag, or a breadcrumb in C#, and it has to also show up on a native crash that C# never sees - so each layer observes the other and mirrors changes across. The obvious trap: native tells managed, managed’s own observer tells native, which tells managed… forever. The fix is a small thread-local re-entrancy guard so a change that arrived from a sync doesn’t echo back out.
  • The SDK crashing on its own crash reports. We hook Godot’s error stream to turn engine errors into events - but our own code can log errors, which we’d then capture, which logs more errors. That’s a feedback loop, and done naively under a lock, a deadlock. Making it safe took a recursion-depth guard, filtering out our own log output by prefix, and a hard rule to never emit an error from inside the per-frame handler.
  • Error storms. One bad line in _process() throws the same error every single frame. With no limits you’d ship 60 identical events a second and set the player’s machine on fire, so there’s per-frame caps, a throttling window, and per-source-line dedup - plus deliberately looser limits for the first few frames of startup, where a burst of errors is normal.
  • “Is this even a C# error?” C# exceptions arrive through the same logger as GDScript errors - sometimes tagged with a .cs file, but on the Mono runtime sometimes with an empty file, so we fall back to sniffing the backtrace’s language name to route it to the .NET layer. And then symbolication itself: GDScript source we can pull live from the running script VM, but in an exported .NET game the assemblies live inside Godot’s packed, compressed virtual filesystem - no file on disk for the .NET SDK to open, so every managed frame comes back as unknown_image. The workaround is handing the SDK a fake seekable “file” that reads those bytes back out through Godot’s own filesystem, and only the two small slices it actually needs. The theme across all of these: the friendly one-line API is easy; what’s hard is the plumbing underneath that keeps three runtimes agreeing on what happened, without deadlocking, looping, or lying about where the crash came from.

Key Takeaways
#

  • “One language, one platform” is a fantasy for Godot devs. Design your error handling for the whole grid from day one.
  • GDScript, C#, and native failures are genuinely different - knowing how they differ is the first step to unifying them.
  • Your export targets are where reproducibility goes to die - Proton, Web, Android, and consoles each change what a crash even looks like.
  • GDExtension is a powerful seam for wrapping native capability behind a friendly GDScript/C# API - crash reporting is one worked example of a general pattern.
25 minutes
English

Related

Console Development Unlocked: Bringing Your Godot Game to PS5, Xbox & Switch
2026 Godotfest26 Talk
Cracking open Godot Editor: what it taught me about Godot
2026 Godotfest26 Talk
Godot & Accessibility - Creating the Utmost Freedom
2026 Godotfest26 Talk
Ichor Online: Building a Massive Multiplayer Online RPG in Godot
2026 Godotfest26 Talk
Keynote: Tools Change, Craft Remains - Reflections on Godot, AI and the Future of Making Games
2026 Godotfest26 Talk
Let's Hear It! Enabling True Audio Accessibility in Godot
2026 Godotfest26 Talk
No Big Rewrites! Incrementally Porting GDScript to C++ with GDExtension
2026 Godotfest26 Talk
Small Team, Big Leverage: Using Automation to Build Games Faster
2026 Godotfest26 Talk
TestFlight for Godot Developers: Playtesting Games on Apple Platforms
2026 Godotfest26 Talk