Skip to content

Troubleshooting

TL;DR: Concrete failure modes, how to spot them, and what to change. Grouped by phase: build-time (Unity / CCK publish), runtime (inside CVR), and logic errors (permissions, serialization, networking).

First CCK build hangs at “Installing…”

Section titled “First CCK build hangs at “Installing…””
  • Symptom: progress bar stuck on “Installing .NET SDK” or “Installing WASI SDK” for more than a few minutes on the first build.
  • Cause: %LOCALAPPDATA%/ChilloutVR/CVRBuildTools/ is populated via direct downloads (dotnet.exe ~200 MB, WASI SDK ~150 MB).
  • Fix: Let it finish once; subsequent builds are cached. If the download is genuinely stuck, delete the partial local-dotnet/ / wasi-sdk/ folders and retry — WasmBuildUtility.EnsureDotnetInstalled will redownload.

error CS0103 inside a WasmBehaviour subclass

Section titled “error CS0103 inside a WasmBehaviour subclass”
  • Symptom: Unity’s Console shows The name 'Foo' does not exist in the current context for a type that’s clearly imported.
  • Cause: the WASM publish uses Microsoft.NET.Sdk with a shim assembly set, not Unity’s full UnityEngine DLL. Types outside the shim (e.g. UnityEngine.Input, UnityEngine.Application) fail at the dotnet publish step even if Unity’s in-editor compile accepted them.
  • Fix: Remove the usage, or replace with a bound alternative. See Not Exposed.
  • Symptom: dotnet publish complains about the target framework.
  • Cause: the installed dotnet version doesn’t match the version pinned in WasmBuildUtility.DotnetVersion (10.0.100-preview.5.25277.114 as of the current CCK).
  • Fix: Delete %LOCALAPPDATA%/ChilloutVR/CVRBuildTools/local-dotnet/ and let the build reinstall the exact version.

WasmCompilationException: Failed To Compile Wasm

Section titled “WasmCompilationException: Failed To Compile Wasm”
  • Symptom: CCK build fails with this exception and one or more error CS* lines just above.
  • Cause: your script has compile errors on the WASM side.
  • Fix: scroll up in the Unity console to the error CS* lines; fix them. Common ones:
    • unbound API (see above)
    • unresolved generic method needing reflection under IlcDisableReflection=true
    • async void methods returning Task out of Update / event handler scope
  • Symptom: IL2026 / IL3050 / IL3002 warnings-as-errors about “Using dynamic code”.
  • Cause: something in your code needs runtime reflection, and the default csproj has <IlcDisableReflection>true</IlcDisableReflection>.
  • Fix: pick one:
    • Remove the reflection (preferred — the Unity binding surface almost never needs it).
    • Set CCKWasmProjectDescriptor.enableReflection = true. Module size grows significantly.

WasmAccessDeniedException: Access to member X denied in a Y object context

Section titled “WasmAccessDeniedException: Access to member X denied in a Y object context”
  • Symptom: exception message mentioning object context (not scope or owner).
  • Cause: your script is in the wrong ObjectContext for the API you called. Examples:
    • Avatar script calling LocalPlayer.* or FileStorage.* (both World-only).
    • Prop script calling Prop.Destroy() on another prop (allowed World-only).
  • Fix: move the functionality to a world script, or use a ObjectContext-appropriate API.

WasmAccessDeniedException: ... in a Y scope context

Section titled “WasmAccessDeniedException: ... in a Y scope context”
  • Symptom: the message mentions scope context.
  • Cause: the target object isn’t in your content root. Writing to a Transform whose GameObject is outside your avatar/prop/world root fails.
  • Fix:
    • Don’t target external objects; hold references only to children of your own root.
    • If you really need to read external state, use getter methods (they are usually ScopeContext.Any).

WasmAccessDeniedException: ... in a Y owner context

Section titled “WasmAccessDeniedException: ... in a Y owner context”
  • Symptom: the message mentions owner context.
  • Cause: the API requires you to be the content owner (Avatar: worn by you; Prop: spawned by you). Remote clients running the same script hit this.
  • Fix: gate the call on a local check — for avatars, compare against LocalPlayer.PlayerObject == Avatar.GetWearer(). For props, similar with GetSpawner.
  • Symptom: a method you call appears to have no effect; no exception in the console.
  • Causes:
    1. The host binding is stubbed (FillNonLinkedWithEmptyStubs) because the import isn’t bound. Look up the symbol in CVR-GameFiles/WasmBinder.Links.*/ to confirm.
    2. You defined an event method whose name doesn’t match UnityEvents / GameEvents enum — the build scanner only dispatches events with exact name matches (e.g. OnWorldPermissionsChanged, not OnPermissionsResult).
    3. The UnityEvent persistent call wasn’t rewired. Check Unity console for [RerouteUnityEvents] logs after build; ensure your target is a WasmBehaviour subclass.
  • Fix: match against the enums in CVR.CCK.Wasm/Scripting/Enums/ and the binder folder lists.
  • Symptom: first event runs, subsequent events don’t fire; a TrapException appears once.
  • Cause: epoch overrun (a scripting_call_* took >20 ms) or a WASM trap from the guest (division by zero, OOB memory access, guest exception escape).
  • Fix:
    • Split expensive work across frames.
    • Avoid long for loops over players / world state.
    • Catch expected exceptions inside your handlers so they don’t escape to the host.
  • Symptom: user toggles a permission in the CVR UI; your script doesn’t react.
  • Causes:
    1. Method name doesn’t match the enum exactly. Must be void OnWorldPermissionsChanged().
    2. Method is static, private-but-with-attribute-required-to-detect, or has parameters. The scanner uses BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic so private is fine; but it expects zero parameters for this event.
  • Fix: confirm the signature: void OnWorldPermissionsChanged().

Runtime — silent behaviour changes after rebuild

Section titled “Runtime — silent behaviour changes after rebuild”
  • Symptom: a value you set in the Unity inspector reverts to the default after a CCK build.
  • Cause: the field is public but of an unsupported type, or the containing class lacks [WasmSerialized].
  • Fix: either use a supported type (see Serialization) or add [WasmSerialized] to the class (if it’s a custom type).
  • Symptom: a private counter / cache that should persist across script reloads starts at 0 again.
  • Cause: private fields are not serialized by default.
  • Fix: add [WasmSerialized] to the field.
  • Symptom: a UI-state or debug counter refuses to reset.
  • Cause: public fields serialize by default.
  • Fix: add [NonWasmSerialized] to the field.
  • Symptom: you Networking.SendMessage(...) but no remote client receives it.
  • Causes:
    1. Payload exceeds MTU. Run Networking.WillMessageBeDropped(size) before sending.
    2. Network is saturated. Check Networking.NetworkCloggedPercentage() — back off if > 0.7.
    3. playerIds array references players who’ve left the instance.
  • Fix: split the payload, switch to SendType.Reliable for large messages, or drop-and-retry with latest player list.
  • Symptom: what you meant as “only the instance owner authorizes” runs on every client.
  • Cause: every client runs the same script. Only you, in code, decide who is authoritative.
  • Fix: early-return in the handler unless Networking.GetInstanceOwner() == LocalPlayer.PlayerObject.
  • Cause: either the script isn’t in World context, or WorldPermissions.CurrentPermissions.FileStorageApiAllowed == false.
  • Fix:
    • If avatar/prop script: move file storage to a world script, or network a “save this” message to the world instance owner.
    • If world script: call WorldPermissions.Request(...) once in Start and gate FileStorage calls on CurrentPermissions.FileStorageApiAllowed.
  • Symptom: writes fail once the world has accumulated near its quota.
  • Cause: total bytes in WorldData/{worldId}/* > FileStorageStorageLimit.
  • Fix: delete files before writing, or request a higher quota by resending WorldPermissions.Request with a larger FileStorageStorageLimit.

Persistent call doesn’t reach the script

Section titled “Persistent call doesn’t reach the script”
  • Symptom: the Unity button fires but the method doesn’t run inside WASM.
  • Causes:
    1. The persistent call target is still the WasmBehaviour type instead of the runtime replacement. Check the inspector after build — target should say WasmRuntimeBehaviour.
    2. The method isn’t public on the script. The rewrite code path assumes public methods.
    3. The method has multiple parameters. Only single-argument (or void) methods are rewireable.
  • Fix:
    • Rebuild via the CCK to re-run rewiring.
    • Make the method public.
    • Pack multi-arg calls into a struct passed through a single string/bytes argument.

When reporting an issue, include:

  • CCK version + Unity version.
  • The CCKWasmProjectDescriptor toggles you used.
  • The exact error line from the Unity console (for build issues) or the CVR log (for runtime issues).
  • A minimal repro script (ideally diffed against one of the Examples).