vercel/next.js · error · Error

`after()`: Argument must be a promise or a function

Error message

`after()`: Argument must be a promise or a function

What it means

Thrown by AfterContext.after() when the task argument is neither a thenable (Promise) nor a function. The after() API accepts only these two shapes so it can schedule deferred work after the response is sent. Passing any other type (number, object, string, undefined) is a programming error surfaced synchronously at the call site.

Source

Thrown at packages/next/src/server/after/after-context.ts:72

  public after(task: AfterTask, workUnitStore: WorkUnitStore): void {
    if (this.initialOnCloseError) {
      throw new InvariantError(
        `An onClose call failed, which means after() can't work correctly.`,
        { cause: this.initialOnCloseError.error }
      )
    }

    // Save the workUnitStore so we can switch its phase later.
    this.workUnitStores.add(workUnitStore)

    if (isThenable(task)) {
      this.addThenable(task)
    } else if (typeof task === 'function') {
      // TODO(after): implement tracing
      this.addCallback(task, workUnitStore)
    } else {
      throw new Error('`after()`: Argument must be a promise or a function')
    }
  }

  private addThenable(thenable: PromiseLike<any>) {
    if (!this.waitUntil) {
      errorWaitUntilNotAvailable()
    }
    this.waitUntil(
      new Promise<void>((resolve) => {
        thenable.then(
          () => {
            resolve()
          },
          (error) => {
            resolve()
            this.reportTaskError('promise', error)
          }
        )

View on GitHub (pinned to 0ae8c72462)

Solutions

  1. Pass a function: after(() => { /* deferred work */ }).
  2. Or pass a Promise: after(someAsyncTask()).
  3. Add a TypeScript type annotation using AfterTask<T> so the compiler rejects invalid arguments before runtime.

Example fix

// before
after(undefined)
after({ log: 'done' })
// after
after(() => { console.log('done') })
after(sendAnalytics())
Defensive patterns

Strategy: type-guard

Validate before calling

function validateAfterTask(task: unknown) {
  if (task !== null && (typeof task === 'function' || (typeof task === 'object' && typeof (task as any).then === 'function'))) return
  throw new Error('after() requires a Promise or a function')
}

Type guard

import type { AfterTask } from 'next/server'
function isAfterTask(t: unknown): t is AfterTask {
  return typeof t === 'function' || (t != null && typeof (t as any).then === 'function')
}

Prevention

When it happens

Trigger: Calling after(someValue) where someValue is not a Promise and not a function — e.g. after(undefined), after({ foo: 1 }), after(42), or after(null). The isThenable and typeof === 'function' checks both fail and the error is thrown.

Common situations: Forgetting to pass an argument; passing a callback's return value instead of the callback; passing an already-awaited result; refactor that changes the argument type.

Related errors


AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06). Data as JSON: /api/errors/14823b09d4b38e7f. Report an issue: GitHub.