JuliusBrussee/caveman · error

withTask: a callback is required

Error message

withTask: a callback is required

What it means

withTask binds a task context (and optional session) to an async scope so the Caveman span processor can stamp caveman.task.id / caveman.session.id on Mastra spans. It supports two call shapes — withTask(id, fn) and withTask(id, sessionId, fn) — and resolves the callback as 'the function-typed argument'. If neither the second nor third argument is a function (missing callback, or a second non-function arg with no third), it throws this validation error before doing anything else.

Source

Thrown at packages/mastra/src/index.ts:158

/** The task context of the currently executing async scope, if any. */
export function currentTask(): TaskContext | undefined {
  return taskStorage.getStore();
}

/**
 * Runs `fn` with a task (and optional session) bound to the async scope, so the
 * span processor can stamp `caveman.task.id` / `caveman.session.id` onto every
 * Mastra span the agent or workflow produces underneath it.
 */
export function withTask<T>(taskId: string, fn: () => T): T;
export function withTask<T>(taskId: string, sessionId: string | undefined, fn: () => T): T;
export function withTask<T>(
  taskId: string,
  sessionIdOrFn: string | undefined | (() => T),
  maybeFn?: () => T,
): T {
  const fn = typeof sessionIdOrFn === "function" ? sessionIdOrFn : maybeFn;
  if (typeof fn !== "function") throw new Error("withTask: a callback is required");
  const id = requireNonEmpty(taskId, "withTask: taskId");
  const sessionId = typeof sessionIdOrFn === "function" ? undefined : sessionIdOrFn;
  const context: TaskContext = sessionId ? { taskId: id, sessionId } : { taskId: id };
  return taskStorage.run(context, fn);
}

// ---------------------------------------------------------------------------
// 3. Span processor
// ---------------------------------------------------------------------------

/** The structural slice of an OTel span this processor touches. */
export interface MastraSpanLike {
  name?: string;
  attributes?: Record<string, unknown>;
  setAttribute?(key: string, value: string | number | boolean): unknown;
  spanContext?(): { traceId?: string; spanId?: string };
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass the callback as the last argument: withTask(taskId, () => runAgent()) or withTask(taskId, sessionId, () => runAgent()).
  2. If the session is conditional, pass `undefined` explicitly in the 3-arg form rather than omitting the callback.
  3. Let TypeScript check the overloads — avoid annotating the call site as any.

Example fix

// before
withTask(task.id);            // callback forgotten
// after
withTask(task.id, () => myAgent.generate(prompt));
// or with session
withTask(task.id, session.id, () => myAgent.generate(prompt));
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the last argument is callable before invoking
function hasCallback(...args: unknown[]): boolean {
  return typeof args[args.length - 1] === "function";
}

Type guard

// Rely on the exported overloads; narrow at the call site
function isWithTaskArgs(args: unknown[]): args is [string, () => unknown] | [string, string | undefined, () => unknown] {
  const last = args[args.length - 1];
  return typeof args[0] === "string" && typeof last === "function";
}

Prevention

When it happens

Trigger: withTask('t1') with no callback; withTask('t1', sessionId) where sessionId was passed but fn forgotten; or passing options objects in place of the callback.

Common situations: Refactoring from withTask(id, fn) to the session variant and dropping fn during the edit, optional-chaining a callback that is undefined (withTask(id, maybeSession, cb?.bind(x))), or TypeScript `any` masking a wrong signature at runtime.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/295c338497ae8db1. Report an issue: GitHub.