Skip to content

Troubleshooting

James Daley edited this page Jun 18, 2026 · 3 revisions

Troubleshooting

Start with the runtime phase where the symptom appears: discovery, registration, compatibility, entitlement, registry resolution, lifecycle, or UI surface rendering.

Triage Flow

flowchart TD
    Symptom["Observed failure"] --> Discover{"Manifest discovered?"}
    Discover -- no --> ManifestFix["Check bundle path and JSON schema"]
    Discover -- yes --> Registered{"Registration record current?"}
    Registered -- no --> RegistrationFix["Refresh discovery and inspect registration store"]
    Registered -- yes --> Compatible{"Compatibility report clean?"}
    Compatible -- no --> CompatFix["Fix schema, manifest template, platform, version, or capability"]
    Compatible -- yes --> Unlocked{"Entitlement unlocked?"}
    Unlocked -- no --> EntFix["Check iapProductID and provider state"]
    Unlocked -- yes --> Factory{"Registry factory found?"}
    Factory -- no --> RegFix["Register matching entryPoint"]
    Factory -- yes --> RuntimeReq{"Runtime requirements satisfied?"}
    RuntimeReq -- no --> ReqFix["Check capabilities, providers, roles, and UI declarations"]
    RuntimeReq -- yes --> Lifecycle{"start(context) succeeds?"}
    Lifecycle -- no --> StartFix["Inspect module error logs"]
    Lifecycle -- yes --> UI{"UI visible?"}
    UI -- no --> UIFix["Check active UI module, viewID, slot, and registry"]
    UI -- yes --> Done["Runtime path healthy"]
Loading

Common Symptoms

Symptom Likely Cause Fix
No modules discovered Manifest files are not bundled under ForsettiManifests. Check target membership and resource path.
validationFailed Manifest JSON is malformed, missing required fields, or declares an unsupported schema/template combination. Validate JSON and compare with the schema page.
duplicateModuleID Two manifests share the same moduleID. Make module IDs globally unique.
registrationMissing Activation ran without a persisted registration record for the manifest. Run discovery through boot or discoverModules before activation and verify the configured ModuleRegistrationStore.
registrationMismatch Stored registration hash or identity fields no longer match the current manifest. Rediscover manifests and inspect whether manifest identity changed unexpectedly.
incompatible(report:) Platform/version/capability check failed. Inspect compatibility issues and fix the manifest or policy.
moduleLocked Entitlement provider returned locked. Verify iapProductID, StoreKit state, or static unlock IDs.
moduleIdentityMismatch Factory returned a module whose descriptor or manifest does not match the discovered manifest. Align descriptor, bundled manifest, manifest property, and registry entry point.
missingCapability Module contributed UI without the required capability. Add the required capability to the manifest or remove the contribution.
unsatisfiedRuntimeRequirement Required provider, default role, data isolation mode, or UI declaration is missing. Check runtimeRequirements, host services, discovered default-role modules, and UI contribution IDs.
notUIModule Manifest says ui or app, but factory returned a plain ForsettiModule. Return a ForsettiUIModule or ForsettiAppModule.
Active UI module not visible Activation did not succeed or injected view is not registered. Check activeUIModuleID, activation errors, and viewID registration.
Theme mask does not appear Default ModuleManager sanitizes themeMask before surface publication. Implement host-owned theming or custom runtime behavior intentionally.
Toolbar route fails Pointer/route ID missing, undeclared, or destination/slot unsupported. Check runtimeRequirements.ui, active OverlaySchema, and host catalogs.
Module messages not received Target payload does not match subscriber module ID or guard denied the message. Use sendMessage(to:type:payload:) and subscribe with the target module ID.

Manifest Debug Checklist

flowchart LR
    Path["Resource path"] --> Fields["Required root fields"]
    Fields --> Template["schema/template pairing"]
    Template --> Types["moduleType and platforms"]
    Types --> Version["Forsetti version range"]
    Version --> Caps["capabilitiesRequested"]
    Caps --> Runtime["runtimeRequirements"]
    Runtime --> Entry["entryPoint"]
Loading

Required manifest root keys:

  • schemaVersion
  • moduleID
  • displayName
  • moduleVersion
  • moduleType
  • supportedPlatforms
  • minForsettiVersion
  • capabilitiesRequested
  • entryPoint

Current modules should also include:

  • manifestTemplateVersion
  • defaultModuleRole when the module provides a default role
  • runtimeRequirements

Runtime Requirement Debugging

Requirement Area What to Check
I/O Every runtimeRequirements.io.kind has the matching capability in capabilitiesRequested.
Required providers Host ForsettiServiceContainer or discovered default-role modules provide required services.
Data isolation framework_mediated_shared has a shared_database provider.
Default roles Role is valid for moduleType and service roles request the matching capability.
UI declarations Every toolbar item, view ID, slot ID, route ID, pointer ID, and theme ID is listed under runtimeRequirements.ui.

Logs to Check

Log Source Useful For
ConsoleForsettiLogger in debug builds Activation, deactivation, denied service resolution, and module lifecycle messages.
ForsettiHostController.errorMessage User-visible controller failures.
ForsettiHostOverlayRouter.lastOutcome Pointer and route resolution failures.
Compatibility report Exact reason activation cannot proceed.
Platform logger Production-capable runtime diagnostics where OSLog is available.

Recovery Pattern

  1. Reproduce with the smallest manifest and module set.
  2. Confirm the manifest loads.
  3. Confirm the registration record exists and matches.
  4. Confirm compatibility is clean.
  5. Confirm entitlement unlock state.
  6. Confirm the registry entry point.
  7. Confirm runtime requirements and provider availability.
  8. Confirm module lifecycle behavior.
  9. Confirm UI contribution rendering.
  10. Add a focused regression test.

Clone this wiki locally