clockworklabs/SpacetimeDB · error · InvalidFunctionArguments

failed to typecheck args

Error message

failed to typecheck args

What it means

FunctionArgs converts incoming call arguments into a typed tuple matching the reducer's signature. The Nullary variant means no arguments were supplied at all; if the reducer's parameter list is non-empty, typechecking cannot succeed and this error is returned before the reducer executes.

Source

Thrown at crates/core/src/host/mod.rs:75

        self._into_tuple(seed).map_err(|err| InvalidFunctionArguments {
            err,
            function_name: seed.name().clone().into(),
        })
    }
    fn _into_tuple<Def: FunctionDef>(self, seed: ArgsSeed<'_, Def>) -> anyhow::Result<ArgsTuple> {
        Ok(match self {
            FunctionArgs::Json(json) => ArgsTuple {
                tuple: from_json_seed(&json, SeedWrapper(seed))?,
                bsatn: OnceCell::new(),
                json: OnceCell::with_value(json),
            },
            FunctionArgs::Bsatn(bytes) => ArgsTuple {
                tuple: seed.deserialize(bsatn::Deserializer::new(&mut &bytes[..]))?,
                bsatn: OnceCell::with_value(bytes),
                json: OnceCell::new(),
            },
            FunctionArgs::Nullary => {
                anyhow::ensure!(seed.params().elements.is_empty(), "failed to typecheck args");
                ArgsTuple::nullary()
            }
        })
    }
}

#[derive(Debug, Clone)]
pub struct ArgsTuple {
    tuple: ProductValue,
    bsatn: OnceCell<Bytes>,
    json: OnceCell<ByteString>,
}

impl ArgsTuple {
    pub fn nullary() -> Self {
        ArgsTuple {
            tuple: spacetimedb_sats::product![],
            bsatn: OnceCell::with_value(Bytes::new()),

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Check the reducer's signature via generated types or module schema and supply the required arguments.
  2. Regenerate client bindings after every module change so signatures match.
  3. For HTTP calls, send arguments as the JSON array in the request body.
  4. For SDK calls, pass the typed arguments the generated client requires.

Example fix

// before — reducer now takes (amount: number)
await db.reducers.giveGold();
// after
await db.reducers.giveGold(100);
Defensive patterns

Strategy: type-guard

Validate before calling

import type { Reducers } from './module/client_types';
function arityMatches(fn: (...args: unknown[]) => unknown, args: unknown[]): boolean {
  return fn.length === args.length;
}

Type guard

type ReducerArgs<R extends keyof Reducers> = Reducers[R] extends (arg: infer A) => void ? A : never;
function argsMatchReducer<R extends keyof Reducers>(name: R, args: unknown[], defs: Record<string, { params: string[] }>): boolean {
  return defs[name as string].params.length === args.length;
}
if (!argsMatchReducer('give_gold', [100], moduleDefs)) throw new Error('arg count mismatch for give_gold');

Try / catch

try {
  await db.reducers.give_gold(100);
} catch (e) {
  if (String(e).includes('failed to typecheck args')) {
    throw new Error('reducer signature changed — regenerate client bindings and fix the call');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling a reducer that declares parameters with zero arguments (empty args in a client call, or an HTTP /v1/database/{db}/call/{reducer} request with no argument payload); a stale generated client calling a module whose reducer gained parameters after a republish.

Common situations: Module updated to require new args but client bindings not regenerated; forgetting the JSON body in manual curl calls; passing args in the wrong location (query string instead of body).

Related errors


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