clockworklabs/SpacetimeDB · error

Reducer `{name}` not found

Error message

Reducer `{name}` not found

What it means

When the scheduler converts a requested call into reducer-call parameters (function_to_reducer_call_params), it looks the name up in the deployed module's definition via reducer_by_name_with_module, resolving type references in the owning (sub)module's typespace. A miss means the module currently deployed to this database has no reducer with that name, so nothing can be scheduled; the request fails at name resolution and never reaches user code.

Source

Thrown at crates/core/src/host/scheduler.rs:840

            function_to_call_params(module, &function_name, args, None)?
        }
    }))
}

fn function_to_reducer_call_params(
    module: &ModuleInfo,
    name: &str,
    args: FunctionArgs,
    at: Option<Timestamp>,
) -> anyhow::Result<(Timestamp, Instant, CallReducerParams)> {
    let identity = module.database_identity;

    // Find the reducer and deserialize the arguments.
    // Use the owning module's typespace (not necessarily the root's) so that type-index
    // references inside the def are resolved correctly for submodules.
    let module = &module.module_def;
    let Some((id, def, owning)) = module.reducer_by_name_with_module(name) else {
        return Err(anyhow!("Reducer `{name}` not found"));
    };
    let args = args.into_tuple_for_def(owning, def).map_err(InvalidReducerArguments)?;

    let (ts, instant) = scheduled_call_time(at);
    Ok((ts, instant, CallReducerParams::from_system(ts, identity, id, args)))
}

fn function_to_procedure_call_params(
    module: &ModuleInfo,
    name: &str,
    args: FunctionArgs,
    at: Option<Timestamp>,
) -> anyhow::Result<(Timestamp, Instant, CallProcedureParams)> {
    let identity = module.database_identity;

    let module = &module.module_def;
    let Some((id, def, owning)) = module.procedure_by_name_with_module(name) else {
        return Err(anyhow!("Procedure `{name}` not found"));

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Check the deployed module's reducer list (module info / generated bindings) and use the exact current name, including any submodule namespace.
  2. If the reducer was renamed, republish keeping a thin wrapper under the old name, or clear the stale scheduled entries and re-schedule.
  3. Redeploy the module revision that actually contains the requested reducer.
  4. After every publish, re-verify crontab and persisted schedule entries against the new module definition.

Example fix

// before: schedule references a reducer that was removed in the latest publish
schedule("cleanOld", args, None)?;

// after: name matches the deployed module_def
schedule("purge_expired", args, None)?;
Defensive patterns

Strategy: validation

Validate before calling

// Validate the name against the deployed module before scheduling
let known: HashSet<_> = module_def.reducer_names().collect();
anyhow::ensure!(known.contains(name), "reducer {name} not in deployed module");

Try / catch

let err = schedule_call(...).unwrap_err();
if err.to_string().starts_with("Reducer `") {
    // stale schedule entry or renamed reducer — fix configuration, do not retry
}

Prevention

When it happens

Trigger: Scheduling a reducer by a name absent from module_def: persisted scheduled/crontab entries from an older module after the reducer was renamed or deleted; a typo'd reducer name in a scheduling request; client or SDK code newer than the deployed module referencing reducers that do not exist yet.

Common situations: Renaming or removing a reducer between publishes while stale schedule entries still reference the old name; rolling back to an older module revision than scheduling code expects; submodule moves that changed a reducer's canonical name.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/4cca251a8df3a639. Report an issue: GitHub.