feat(switch2): Nintendo Switch 2 support, and stop the offline transport raising system dialogs - #2831
feat(switch2): Nintendo Switch 2 support, and stop the offline transport raising system dialogs#2831bitsandfoxes wants to merge 7 commits into
Conversation
UnityWebRequestTransport opens a connection per envelope. On Nintendo Switch every attempt made while the console is offline raises the system "connect to the internet" dialog, and because logs and metrics each flush on a 5s timer that turned into a prompt every couple of seconds. A connection error previously only logged a warning and returned null, which also skipped the rate-limit handling in HttpTransportBase, so nothing anywhere recorded that the network was down. Add two guards to SendEnvelopeAsync: skip sending entirely while the platform reports NotReachable, and back off exponentially (1s up to 60s) after a connection error, resetting on the first success. The backoff is what covers platforms whose reachability cannot be trusted, and the case where the network drops after startup. Both paths record DiscardReason.NetworkError so client reports still account for the drops. Reachability goes through IApplication rather than UnityEngine.Application so the behaviour is testable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
Callers log from inside catch blocks that have already handled the failure. UnityLogger interpolated the exception directly, so anything that threw while being stringified escaped from within the handler and turned a handled error into a fatal one. On Nintendo Switch that is what happened: with DisableFileWrite set, resolving the installation ID falls through to NetworkInterface.GetAllNetworkInterfaces(), whose static initializer throws. Hub.ConfigureScope caught it and logged it, but stringifying that exception threw again, escaping the handler and aborting SDK initialization partway through registering integrations. Every Unity integration after GlobalRootScopeIntegration was left unregistered, so nothing was captured and no envelope was ever produced. Format the message and render the exception defensively, falling back to the type and message when the stack trace cannot be read, and never let the write to Unity's logger propagate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
Switch 2 reached SentryNativeSwitch.Configure but bailed out of it with "Native support is disabled for 'Switch2'": IsNativeSupportEnabled had no case for the platform, so it fell through to the default and returned false. Native support could never initialize, no matter how the project was configured. Switch 2 shares the Switch implementation, so it now follows SwitchNativeSupportEnabled, and the existing Switch assembly is shipped to it by enabling that platform on the assembly's plugin metadata - which is what decides where an assembly goes, so no separate Switch 2 assembly is needed. RuntimePlatform.Switch2 only exists in Unity 6000.3 and newer while the SDK still supports 2021.3, and these assemblies compile against a single Unity version, so the platform is matched by name instead of by enum member - referencing the member directly would stop the SDK building against the editors that predate it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
…chability Application.internetReachability reports a Switch console as reachable while it is offline - the device logs show "Reachability reported as ReachableViaLocalAreaNetwork" next to every failed send. The reachability gate in the transport was therefore inert on the one platform it was meant to protect, leaving the connection-error backoff to absorb everything. Since attempting the connection is itself what raises the system "connect to the internet" dialog, that still meant a dialog per attempt, just at the backoff's cadence. Let the platform answer instead. sentry-switch now exposes sentry_switch_utils_is_network_available(), which asks the console's network interface manager without submitting a network request, so asking costs nothing and shows no dialog. SentryNativeSwitch hands it to the options as NetworkAvailabilityProbe and the transport prefers it, falling back to reachability where no probe is set - which keeps WebGL and the unknown-platform path behaving as before. The stub returns 1 so builds without the native library keep today's behaviour and rely on the backoff. Requires a sentry-switch build that exports the new function: the P/Invoke goes through __Internal against a statically linked archive, so it resolves at link time rather than falling back at runtime. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
Recognise the platform and route it through the existing Switch implementation: UNITY_SWITCH2 defines SENTRY_NATIVE_SWITCH2, which selects SentryNativeSwitch as the platform configuration, and both assembly definitions now include Switch 2 so the runtime code ships to it. Switch 2 gets its own native plugin directory with no-op stubs, matching the Switch layout, so a project without the native library still links. Switch 2 shares the SwitchNativeSupportEnabled option rather than introducing a second one, since it shares the implementation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
| return; | ||
| } | ||
|
|
||
| // Wired up before the storage and native SDK setup below: the probe only needs the native | ||
| // library to be linked, so the transport keeps the benefit even if either of those fails. | ||
| Logger?.LogDebug("Using the native SDK to determine network availability."); |
There was a problem hiding this comment.
Bug: An early return in SentryNativeSwitch.Configure prevents setting the NetworkAvailabilityProbe on Switch when native support is disabled, causing an unreliable network check.
Severity: MEDIUM
Suggested Fix
Move the options.NetworkAvailabilityProbe = IsNetworkAvailable; assignment to before the if (!options.IsNativeSupportEnabled(platform)) check. This will ensure the reliable network probe is always configured for the Switch platform, regardless of the native support setting.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: src/Sentry.Unity.Native/SentryNativeSwitch.cs#L85-L90
Potential issue: In `SentryNativeSwitch.Configure`, an early `return` statement is
executed if `options.IsNativeSupportEnabled(platform)` is false. This prevents the
`options.NetworkAvailabilityProbe` from being set to the custom `IsNetworkAvailable`
function for the Nintendo Switch platform. Consequently, the transport layer falls back
to using `Application.internetReachability`, which is known to be unreliable on the
Switch and can incorrectly report the device as online. This defeats the purpose of a
fix designed to prevent offline error dialogs from appearing when the console is not
connected to the internet.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1ef9065. Configure here.
The Switch 2 stub was a byte-for-byte copy of the Switch one, and it had already drifted: the network availability function added in this branch went into the Switch stub only. Which platforms a stub ships to is decided by its plugin metadata, exactly as it is for the assembly, so one file can serve both. It also could not have worked as it stood. The stub is enabled or disabled at build time depending on whether the user supplied the native libraries, and SwitchNativePluginBuildPreProcess returned early for anything that was not BuildTarget.Switch - so on Switch 2 the stub was never toggled at all, leaving a project without the native libraries unable to link. And because package-dev metadata is gitignored except for named exceptions, the copy's .meta was not tracked, so the duplicate shipped without the import settings that gate it. The preprocessor now runs for both targets, resolves the required libraries per platform, and toggles compatibility for whichever target is being built - the importer tracks that per target, so the two do not interfere. BuildTarget.Switch2 only exists in Unity 6000.3 and newer, so it is matched by name and the value taken from the build report, which keeps this compiling against older editors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8
|
Split into three focused PRs, since these are independent changes that happened to be found together:
Nothing is dropped; the combined branch is superseded by those three. |

Brings up Nintendo Switch 2, and fixes three defects found while getting it working on a devkit. Each commit stands alone.
The reported problem
A Switch game that is offline showed the system "connect to the internet" dialog every couple of seconds, indefinitely.
UnityWebRequestTransportopens a connection per envelope, and on Switch the attempt itself is what raises the dialog. With logs and metrics both flushing on a 5s timer, that is a dialog every ~2.5s. A connection error only logged a warning and returnednull, which also skipped the rate-limit handling inHttpTransportBase— so nothing anywhere recorded that the network was down, and the next envelope tried again immediately.What's here
fix(switch): transport backoff — skip sending while the platform reports no network, and back off exponentially (1s→60s) after a connection error, resetting on the first success. Both paths recordDiscardReason.NetworkError, so drops still show up in client reports instead of vanishing.fix(switch): diagnostic logger no longer throws while logging — this one bit hard. WithDisableFileWrite, resolving the installation ID falls through toNetworkInterface.GetAllNetworkInterfaces(), whose static initializer throws on Switch.Hub.ConfigureScopecaught it and logged it — but stringifying that exception threw again, escaping the handler and aborting SDK init partway through registering integrations. Every Unity integration afterGlobalRootScopeIntegrationwas left unregistered, so nothing was captured and no envelope was ever produced. A logger must never turn a handled error into a fatal one.fix(switch2)+feat(switch2): Switch 2 support —UNITY_SWITCH2selectsSentryNativeSwitch, both asmdefs include the platform, and it gets its own plugin directory with no-op stubs.IsNativeSupportEnabledhad noSwitch2case, so it fell through to the default and returnedfalse— native support could never initialize regardless of configuration. Switch 2 sharesSwitchNativeSupportEnabledand the existing Switch assembly, which is shipped to it via plugin metadata rather than by building a second assembly.fix(switch): ask the platform for network availability — see below.Why reachability isn't enough
Application.internetReachabilityreports a Switch console as reachable while it is offline. From the device:So the reachability gate was inert on the one platform it was written for, and the backoff absorbed everything — which reduced the dialogs to one per minute but could not remove them, because each retry is still an attempt.
sentry-switch now exposes
sentry_switch_utils_is_network_available(), which asks the console's network interface manager without submitting a network request — so asking costs nothing and shows no dialog.SentryNativeSwitchsupplies it asNetworkAvailabilityProbeand the transport prefers it, falling back to reachability when no probe is set, leaving WebGL and the unknown-platform path unchanged.Verified on a Switch 2 devkit
Offline, with the probe in place:
No
Failed to send request, no backoff, no dialogs — noUnityWebRequestis ever constructed, so nothing can raise one. Native init completes and scope sync is syncing breadcrumbs, user and trace.Merge order
Depends on getsentry/sentry-switch#188, which must merge and ship first. The P/Invoke goes through
__Internalagainst a statically linked archive, so it resolves at link time — a Switch player built against an olderlibsentry.afails to link rather than falling back. The bundled stubs only cover the no-native-library case.Not covered here
Two related issues left deliberately out of scope:
SentryNativeBridgenever setsnetwork_connect_func, so the native transport does not start and native crash reports depend on Nintendo's upload path; andHub's integration loop has no per-integrationtry/catch, which is why a single throwingRegistercould decapitate the SDK.🤖 Generated with Claude Code
https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8