loco-rs/loco · error
ServerOnly boot always builds a router
Error message
ServerOnly boot always builds a router
What it means
The generated AWS Lambda handler boots the app with StartMode::ServerOnly and then unwraps boot.router with an expect stating 'ServerOnly boot always builds a router'. ServerOnly mode is supposed to always construct the router, so a None here is an internal invariant violation — either the boot failed partially, the app's Hooks::routes returned nothing, or the generated code was edited/uses an incompatible loco version where ServerOnly no longer guarantees a router.
Solutions
- Verify loco_rs version matches the generated template's expectation (cargo loco doctor; compare Cargo.lock) and regenerate the deployment with `cargo loco generate deployment`.
- Ensure your Hooks::routes implementation returns a valid Routes and app boot is unmodified for ServerOnly mode.
- Inspect create_app return: if boot.router can legitimately be None in your version, handle it explicitly instead of unwrapping.
- Diff your lambda handler against a freshly generated template to spot accidental edits to the boot sequence.
- Enable/configure the server feature so ServerOnly boot actually constructs the router.
Example fix
// before
let router = boot
.router
.expect("ServerOnly boot always builds a router");
// after
let router = boot.router.ok_or_else(|| {
anyhow::anyhow!("loco ServerOnly boot did not build a router; check Hooks::routes and loco version")
})?; Defensive patterns
Strategy: type-guard
Validate before calling
// before unwrap debug_assert!(boot.router.is_some(), "ServerOnly boot must build a router; check loco version and Hooks::routes");
Type guard
fn require_router(boot: Boot) -> axum::Router {
match boot.router {
Some(r) => r,
None => panic!("ServerOnly boot returned no router — verify loco_rs version and Hooks::routes"),
}
} Try / catch
let router = match boot.router {
Some(r) => r,
None => {
eprintln!("lambda boot: no router built; check loco version and Hooks::routes");
return Err(anyhow::anyhow!("no router built during ServerOnly boot"));
}
}; Prevention
- Keep loco_rs and the loco CLI versions aligned with the template that generated the handler
- Regenerate deployment files after framework upgrades instead of hand-editing
- Keep Hooks::routes returning a non-empty Routes for server boots
- Diff customized lambda handlers against freshly generated templates after upgrades
When it happens
Trigger: Deploying the lambda template and having create_app::<App>(StartMode::ServerOnly, ...) return a boot struct whose .router is None — e.g. hooks misconfigured so no router is built, a loco version mismatch where the ServerOnly contract changed, or hand-edited generated main/lambda code that altered the boot path.
Common situations: Pinning a newer/older loco_rs than the template expects; customizing Hooks::routes or boot logic and accidentally skipping router construction; copy-pasting the lambda template into an app whose boot differs from the scaffolded one; feature flags (e.g. no server feature) changing create_app behavior.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Type mismatch in RefGuard
- failed to install signal handler
- logger initialization failed
- fs service should build with success
- memory service must build with success
AI-assisted analysis of loco-rs/loco@23639d1e36 (2026-09-12).
Data as JSON: /api/errors/295ba1241848702a.
Report an issue: GitHub.
Appendix: source
Thrown at loco-gen/src/templates/deployment/lambda/lambda.t:67
use loco_rs::boot::{create_app, StartMode};
use loco_rs::environment::{resolve_from_env, Environment};
{%- if db %}
use migration::Migrator;
{%- endif %}
use {{pkg_name}}::app::App;
#[tokio::main]
async fn main() -> Result<(), Error> {
let environment: Environment = resolve_from_env().into();
let config = App::load_config(&environment).await?;
{%- if db %}
let boot = create_app::<App, Migrator>(StartMode::ServerOnly, &environment, config).await?;
{%- else %}
let boot = create_app::<App>(StartMode::ServerOnly, &environment, config).await?;
{%- endif %}
let router = boot
.router
.expect("ServerOnly boot always builds a router");
run(router).await
}
View on GitHub (pinned to 23639d1e36)