medusajs/medusa · error · NonSerializableCheckPointError

Unable to serialize context object. Please make sure the wor

Error message

Unable to serialize context object. Please make sure the workflow input and steps response are serializable.

What it means

When a workflow checkpoints itself (long-running/async workflows that save state between steps), DistributedTransaction must serialize its whole context (workflow input plus every step response) to JSON for storage. If any value in that context is not serializable (BigInt, circular reference, class with toJSON that throws, etc.), NonSerializableCheckPointError is thrown so the state is not silently corrupted.

Source

Thrown at packages/core/orchestration/src/transaction/distributed-transaction.ts:710

  }

  public hasTemporaryData(key: string) {
    return this.#temporaryStorage.has(key)
  }

  /**
   * Try to serialize the checkpoint data
   * If it fails, it means that the context or the errors are not serializable
   * and we should handle it
   *
   * @internal
   * @returns
   */
  #serializeCheckpointData() {
    try {
      JSON.stringify(this.context)
    } catch {
      throw new NonSerializableCheckPointError(
        "Unable to serialize context object. Please make sure the workflow input and steps response are serializable."
      )
    }

    let errorsToUse = this.getErrors()
    try {
      JSON.stringify(errorsToUse)
    } catch {
      // Sanitize non-serializable errors
      const sanitizedErrors: TransactionStepError[] = []
      for (const error of this.errors) {
        try {
          JSON.stringify(error)
          sanitizedErrors.push(error)
        } catch {
          sanitizedErrors.push({
            action: error.action,
            handlerType: error.handlerType,

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Sanitize the workflow input: convert BigInt to string, replace Map/Set with arrays/objects before running the workflow
  2. Make step responses return plain serializable DTOs instead of entity instances
  3. If you need dates/BigInt, convert at the boundary (e.g. value.toString()) and reconstruct in later steps
  4. Test your workflow input with JSON.stringify before invoking it

Example fix

// before
const input = { productId: 123n } // BigInt → serialization fails on checkpoint
await myWorkflow(req.scope).run({ input })

// after
const input = { productId: "123" } // string survives JSON checkpointing
await myWorkflow(req.scope).run({ input })
Defensive patterns

Strategy: validation

Validate before calling

// Validate workflow input is JSON-safe before running a checkpointable workflow
function assertSerializable(obj, seen = new WeakSet()) {
  if (obj === null || typeof obj !== 'object') {
    if (typeof obj === 'bigint') throw new Error('BigInt not serializable')
    return
  }
  if (seen.has(obj)) throw new Error('Circular reference')
  seen.add(obj)
  for (const v of Object.values(obj)) assertSerializable(v, seen)
}
assertSerializable(input)
await myWorkflow(container).run({ input })

Try / catch

try { await workflow.run({ input }) } catch (e) { if (e.name === 'NonSerializableCheckPointError') { /* sanitize input: String(bigints), strip entities, rerun */ } throw e }

Prevention

When it happens

Trigger: Calling saveCheckpoint (explicitly, or automatically when a workflow suspends with .requestTimeout/waitFor or when the engine checkpoints after each step) while the workflow input or a step's response contains a non-JSON-serializable value such as BigInt, a circular object graph, or a function/Symbol-bearing payload.

Common situations: Passing ORM entities, Map/Set, or IDs as BigInt into workflows; a step returning a model instance with circular relations; enabling async/checkpointed workflows on data that previously only flowed in-memory.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/b095c5ae861f9b35. Report an issue: GitHub.