Code addon troubleshooting
Trace activation, dependency, readiness, registry, network, storage and teardown failures from their actual contracts.
Activation failures
| Symptom | Check |
|---|---|
| No implementation found | The class is public, implements ILitherarpAddon, has [LitherarpAddon("id")], and the published revision includes its .cll. |
| Identity mismatch | Attribute, AddonId, manifest AddonId and operator entry id normalize to the same value. |
| Protocol mismatch | Implementation and manifest ProtocolVersion are equal and positive. |
| Dependency plan rejected | Read lastMessage for missing required ids, invalid SemVer constraints, conflicts or a cycle. |
| Code gate refused | Global allowCodeAddons, per-entry allowCode, enabled flags and parent package are all correct. |
| Revision drift | Reapply the manifest revision and confirm required package revisions. Do not mix a stale package cache with a newer operator entry. |
| Loading timed out | Each async loading callback must finish within 15 seconds and honor its cancellation token. |
| Package operation timed out | Package fetch, mount, manifest and cloud-resource operations each have a fixed 30-second budget. Check connectivity, package visibility and the pinned revision before retrying. |
Client readiness
| Symptom | Check |
|---|---|
| Client waits before spawn | The client is mounting the exact required package and code snapshot. Inspect client logs for the first required failure. |
| Client is kicked on timeout | Increase clientReadinessTimeoutSeconds only after measuring legitimate load time. The valid range is 15 to 300. |
| Client reports a different fingerprint | A hot apply superseded its older snapshot. Let the new generation finish; stale acknowledgements are ignored. |
| Local package works only for host | This is expected. Remote clients never mount local.*. Publish a revision for multi-client tests. |
Runtime behavior
| Symptom | Check |
|---|---|
| Public section did not change | Call InvalidatePublicSnapshot after provider state changes. Registered config reload callbacks do this automatically. |
| Player section rejected | Measure serialized UTF-8 bytes, not character count. Keep it at or below 8,000 bytes. |
| Network handler never runs | Match addon id, normalized message name, direction and registered realm. Client-to-host delivery needs a positive caller Steam id and rate budget. A caller pawn is required only when PermissionNode is configured. |
| Public snapshot fails | Keep each addon-provided section at or below 8,000 UTF-8 bytes. The core addons section is limited to 4,194,304 bytes and the complete snapshot to 8,388,608 bytes. |
| Event handler stops running | Three consecutive recoverable failures quarantine it. Fix the exception and recreate the subscription by reapplying the addon. |
| Registry layer rejected | The key is valid, priority is between -1000 and 1000, the addon does not already own that key, and another owner does not hold the same key and priority. |
| Versioned load failed | Inspect LitherarpAddonDataResult.Error. Supply a migration for every stored schema version you accept. |
| Admin page is visible but action is denied | Page access and host action authorization are separate. Grant and check the same addon.<id>.<node> on the host. |
Disable and reapply
Confirm that subscriptions were passed to Track, custom objects to TrackGameObject, components to TrackComponent, and background work to TrackCancellation. Use realm-specific shutdown or OnTeardown for anything else.
Mounted bytes and static fields can remain cached until restart. If disabled behavior remains active, first prove whether it is an untracked callback or object. A cached asset byte alone does not retain a context registration.