Skip to content

Troubleshooting

Melih Ercan edited this page Sep 16, 2026 · 2 revisions

Troubleshooting

Symptoms, and what actually causes them. Most entries here are failures that do not name their own cause — that is why they are worth writing down.

Start with Platform prerequisites if you have not read it; three of the entries below are simply that page not having been read.

Nothing happens at all

The app hangs at startup and writes nothing — Windows

The Windows App Runtime is missing. No exception, no log line, no message. Your app stops before Main.

A missing library would have thrown by name. This blocks instead, inside a bootstrapper injected into the module constructor of anything referencing the package. It is the hardest failure on this page to recognise, and the easiest to fix:

<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>

Full explanation, including who has the runtime and who does not, in Platform prerequisites.

Confirm it before you believe it: attach a debugger and break in. The stack will show Bootstrap.TryInitialize under <Module>..cctor().

Nothing works on Blazor, and there is no error

The interop script tag is missing, or it is placed after blazor.webassembly.js.

<script src="_content/WebRTCme/JsInterop.js"></script>

If you are upgrading from 2.0.0, the path changed — it used to be _content/WebRTCme.Bindings.Blazor/JsInterop.js, and that package no longer exists.

The console says JsInterop is not defined

Same cause as above.

Restore and build

error NU1605: Detected package downgrade: Microsoft.Maui.Controls

Expected, and yours to fix — the error does not mention WebRTCme but it is about it. Pin the versions the packages want:

<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.101" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.101" />

The same error appears in the opposite direction if you upgrade WebRTCme and leave an old pin behind. Releases states the version each release wants.

error NETSDK1084: no application host available for the RuntimeIdentifier 'maccatalyst-x64'

You are trying to build a test or console project for Mac Catalyst. Catalyst builds an .app, not an executable, and there is no app host to be had — setting UseAppHost=true produces exactly this. The two requirements cannot both be satisfied; a Catalyst executable has to be an app. See Testing, tier 4.

error NETSDK1100: you must specify a value for the Windows target platform

You asked a multi-targeted project to build on a Mac and it defaulted to a Windows target framework. Confusing when you asked for Catalyst. Note that passing -f or -p:TargetFramework does not fix it: the build honours them and the implicit restore does not, so you then get NETSDK1005 saying the assets file has no target for the framework you just supplied. The project has to pick its own framework.

Everything compiles, but the app finds nothing behind the API on a device

You are on a fallback slice. NuGet falls back: a net10.0-android project restoring a package with no Android slice resolves lib/net10.0/ instead and compiles perfectly, because the API is identical across slices. There is simply no binding behind it.

Check obj/project.assets.json for your target framework and confirm it resolved its own slice rather than net10.0. This is exactly what Tests/Test-Package.ps1 asserts — see Testing.

Undefined symbols for architecture arm64 when the Mac links

You are using project references rather than packages, and the binding's .resources.zip did not reach your app. That zip is what the Apple SDK extracts WebRTC.xcframework from; the bindings are referenced with PrivateAssets="all", which stops it flowing.

Reference the platform binding project directly, as WebRTCme.DemoApp.Maui does. Package consumers do not need this and should not do it — in a package the binding and its zip sit side by side in lib/<tfm>/, which the SDK already understands. See Demo apps.

macOS refuses to codesign an app embedding WebRTC.framework

The framework must be a versioned bundleVersions/A plus symlinks. Git on Windows cannot check those symlinks out without core.symlinks, so the binary arrives as a 23-byte text file holding a path. Every package built on Windows before this release carried exactly that.

If you are building from source on Windows, build-versioned-framework.sh reassembles it. If you are consuming a package, take 26.9.16 or later.

At runtime

DllNotFoundException or TypeInitializationException on CrossWebRtc.Current

The native payload is not beside your app. On Windows that is WebRtcInterop.dll plus its import closure — abseil, BoringSSL, protobuf, libc++ — and the MSVC runtime. They ship at runtimes/win-x64/native/ and should be copied by the SDK.

If you refreshed the native binaries by hand, check you took the whole closure and not just WebRtcInterop.dll.

NotImplementedException from a platform class

That part of the W3C surface is not implemented on that platform. What works where lists what is missing and, more usefully, explains that the count is not the measure — most of it has never been reached by anything.

If you hit one on a path that matters to you, please open an issue. That is the signal that decides what gets filled in next: it means something moved from "nothing calls it" to "something does".

Recording (MediaRecorder) is the most likely one to meet: it is Blazor only.

NullReferenceException in ModelExtensions.ToNative on CreateDataChannel

Fixed in 26.9.15. CreateDataChannel(label) with no options threw on Android, iOS and Mac Catalyst. Upgrade.

The browser says The provided value 'null' is not a valid enum value of type RTCBundlePolicy

Fixed in 26.9.15. Before that, every Blazor consumer had to reach into JSRuntime's non-public JsonSerializerOptions by reflection.

If you see this on 26.9.15 or later, it is a regression — please report it. Do not reinstate the reflection workaround; the fix belongs in the binding.

A call connects, then nothing renders

Separate "arriving" from "drawn". IConnection.GetStats(peerId) tells you whether frames are actually being received; if bytes are climbing and the tile is blank, it is a rendering problem rather than a media one.

On MAUI, check the Media handler is registered:

handlers.AddHandler(typeof(Media), typeof(MediaHandler));

The picture does not rotate when the device does — Android

Known and open. Rotating the device does not rotate the picture locally. The demo apps lock to portrait, which mitigates rather than fixes it.

No library logs on a MAUI device

ILogger calls in the middleware go nowhere on every MAUI platform unless a provider is registered. The MAUI demo does this:

builder.Logging.AddProvider(new ConsoleLoggerProvider());

Calls

It works on my LAN and fails for real users

You need TURN. STUN alone lets a peer discover its public address, which is enough when at least one NAT cooperates. When both peers are behind symmetric NAT — common on mobile networks and corporate wifi — neither can reach the other and the call needs a relay.

The signalling server ships with StunOnlyProxy registered and TURN proxies for Xirsys, Twilio and coturn present but commented out. See Connection: Signaling.

A demo app starts and then does nothing

Its configured server address points at 192.168.1.48, which is a machine on the author's LAN and not yours. Both demos need a server you deploy, and their appsettings.json edited. See Demo apps.

The Blazor demo shows a signalling server down banner for this case.

Two clients join the same room and collide

Peer id is the display name. Give each device a different name.

A peer that closed does not leave the call

A client killed abruptly — swiped away rather than leaving the page — sends no close frame, so the server keeps the peer until its own keepalive notices, which takes minutes. To test peers leaving, leave the call page instead of killing the app.

The mediasoup server refuses the WebSocket with 403

DOMAIN must match the host in the clients' BaseUrl. The server compares the HTTP Origin against its own by scheme and host, and a missing Origin counts as a mismatch. A browser served from localhost and a phone reaching 192.168.1.48 cannot both be accepted at once.

Note that a 403 on https://<host>:4443/rooms/test is the healthy answer for a plain curl — the API is access controlled, so 403 means TLS terminated and the server replied.

Signalling looks healthy on mediasoup but no media flows

Publish every RTC port you created. The server makes one worker per CPU by default and gives each its own port from 44444 upwards, so 32 cores means 44444–44475. NUM_WORKERS is not a performance knob here — it decides how many ports you need. Use NUM_WORKERS=1 for testing.

Also check MEDIASOUP_ANNOUNCED_ADDRESS is an address every client can reach.

Video quality collapses on the SFU

Turn simulcast off. Measured over one LAN: 64–159 kbit/s at 289x240 with simulcast, against 1781–1828 kbit/s at 578x480 without. It is mediasoup's congestion control rather than this client. The demo apps ship with UseSimulcast: false for this reason. See Connection: MediaSoup.

On Android, simulcast does not encode at all with this libwebrtc build.

Android: the call dies when the app goes to the background

Fixed. If you see it, you are on an old build — upgrade.

Still stuck

  • doc/KnownGaps.md in the repository is the long-form engineering record. Entries are kept after they are fixed, with what the fault looked like beforehand — which is often the fastest way to recognise something.
  • Open an issue with your platform, the package version, and the exception or the absence of one. "It hangs with no error" is a useful report here; several things on this page fail exactly that way.

Clone this wiki locally