astrid-runtime/astrid · error

lifecycle dispatch failed

Error message

lifecycle dispatch failed: {e}

What it means

run_lifecycle_in_scope joins the result of the actual lifecycle dispatch (hooks run on a dedicated runtime thread) and, if that future returned Err, wraps it as 'lifecycle dispatch failed: {e}' after tearing down the event bus and runtime. It is a top-level aggregator error: the inner error carries the real cause (hook failure, storage failure, etc.).

Solutions

  1. Read the inner error chained via anyhow's context/cause chain (print with {:#} or .source()) to find the real failing hook or subsystem.
  2. Run the failing lifecycle hook command manually to reproduce and fix the underlying failure.
  3. Verify the capsule's lifecycle hook definitions (Capsule.toml) point to existing, executable commands.
  4. Check storage/secret backend availability for the principal scope before retrying.

Example fix

// before
match result {
    Err(e) => eprintln!("failed"),
    Ok(_) => {}
}
// after
if let Err(e) = result {
    eprintln!("lifecycle failed: {e:#}"); // full cause chain
    for cause in e.chain().skip(1) {
        eprintln!("  caused by: {cause}");
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

match run_lifecycle(&config, capsule_id, step) {
    Ok(_) => {}
    Err(e) => {
        eprintln!("lifecycle dispatch failed: {e:#}");
        // inspect e.chain() to find the failing hook
    }
}

Prevention

When it happens

Trigger: Any failure inside the dispatched lifecycle future for install/update/remove hooks — e.g. a lifecycle command exits non-zero, a hook panics to an error, storage or event-bus setup inside the dispatch fails — surfaced through run_lifecycle / run_lifecycle_for_principal / run_lifecycle_for_principal_with_storage.

Common situations: A capsule's install or remove hook script fails (missing binary, bad exit code); a hook times out or the runtime is dropped mid-dispatch; storage backends are unavailable when hooks try to persist state.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/3b6e62299a95fddb. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-capsule-install/src/lifecycle.rs:304

            phase,
            previous_version,
            principal_context,
        ))
    } else {
        tokio::task::block_in_place(|| {
            handle.block_on(astrid_capsule::engine::wasm::run_lifecycle_for_principal(
                cfg,
                phase,
                previous_version,
                principal_context,
            ))
        })
    };

    drop(event_bus);
    drop(owned_rt);

    result.map_err(|e| anyhow::anyhow!("lifecycle dispatch failed: {e}"))
}

fn lifecycle_kv_namespace(principal: &PrincipalId, capsule_id: &str) -> String {
    format!("{principal}:capsule:{capsule_id}")
}

fn lifecycle_home_root() -> Option<std::path::PathBuf> {
    // Native PrincipalHome is a released import source, never a lifecycle
    // authority. The engine mounts home:// only when its principal context
    // carries the UID-bound AstridFilesystem store.
    None
}

fn lifecycle_config_values(
    principal_uid: Option<astrid_core::identity::PrincipalUid>,
    kv_store: &Arc<dyn astrid_storage::KvStore>,
    capsule_id: &str,
    owned_rt: Option<&tokio::runtime::Runtime>,

View on GitHub (pinned to affd8760f4)