Documentation menu
Troubleshooting
Every entry below is a failure that happened during a real migration, in a seam no unit test reaches. If your app is misbehaving, it is probably here.
Nothing connects
joinChannel fails with 404 page not found
VoxaRtc.serverUrl is a wss:// URL. That tells the SDK it is talking to a raw media server, so it skips the token exchange and sends media-server signaling straight at the gateway, which has no such route.
Point it at the gateway over HTTP: https://gate.zamansheikh.com. The scheme is what selects the flow — http(s) means exchange, ws(s) means connect directly.
errJoinChannelRejected, or the gateway logs “unknown App ID”
The token is signed by credentials the gateway does not know. Either create the project with your existing Agora App ID and certificate, or update your token server to the ones the console shows you. Both paths are in Migrate from Agora.
Token rejected as expired the moment it is minted
Almost always a custom verifier reading Agora's privilege values as absolute unix times. They are relative — seconds after the token's issue timestamp. The gateway handles this correctly; if you have your own verification somewhere in the path, that is the bug.
Media problems
AgoraVideoView stays black forever
Fixed in 0.2.3 — upgrade if you are below it. The cause: views built before joinChannel (preview tiles, seat widgets) subscribed to a transport event stream that does not exist yet, so they never learned their track arrived. Apps caching their VideoViewControllers — the pattern Agora's own docs recommend — never rebuilt.
No camera or microphone permission dialog on Android
Fixed in 0.2.1. Android silently denies permissions that are not declared in the merged manifest, and apps migrating from Agora relied on agora_rtc_engine's manifest to contribute them. The package now ships its own Android module that declares them.
If you are on 0.2.1+ and still see no dialog, your app is not requesting them at runtime — the manifest only makes the request possible.
switchCamera does nothing, or the view keeps showing the old camera
Two separate bugs, both fixed: on a pre-join preview the flip targeted only published tracks (0.2.5), and after a flip the renderer had no reason to rebind because a flip restarts the media track in place rather than replacing the object (0.2.6). On 0.2.6+ the view keys on the underlying track and rebinds.
Speaking indicators / mic-wave UI never light up
Fixed in 0.2.5. onAudioVolumeIndication was reporting the media engine's linear amplitude, which puts ordinary speech at about 8 on Agora's 0–255 scale — well under the "> 30" threshold apps typically use. Volumes are now mapped through dB, so speech lands in Agora's usual 100–200 range.
TrackPublishException on join
The publish grant did not include publish rights. If the project has enforceCoHostAuth enabled, a subscriber-role token cannot publish — mint a publisher token, or turn the flag off to match Agora's default co-host behaviour. See Roles and publish rights.
Audio “does not work” when testing with one device
It probably does work. Speaker-to-microphone on the same device is exactly what echo cancellation exists to remove, so you hear nothing and conclude it is broken. Test with two physical devices, or headphones on one of them.
Build and tooling
Version solve fails after adding voxa_rtc_engine
If wakelock_plus is in the project: the media engine pulls device_info_plus 12.x (win32 5.x) and wakelock_plus wants win32 6.x. Lift device_info_plus, do not pin win32 — Dart type-checks Windows sources even in an Android-only build, so overriding win32 produces analysis errors in files that never run.
dependency_overrides:
device_info_plus: ^13.2.0pub get cannot find the version you just published
pub.dev takes three to four minutes to propagate a new release. Wait, then retry once — do not loop.
Local development
Desktop client cannot reach a LAN address on macOS
macOS gates local-network access behind a permission the Flutter runner may not hold, so 192.168.x.x gateways fail while public ones work. Test desktop builds against your deployed domain rather than a LAN IP.
Still stuck
The most useful thing you can capture is the callback log: debugPrint in every handler you register, one run of the broken flow. Ordering tells you where in the lifecycle it fails, which is usually enough to identify the seam.