{"record":{"id":"14823b09d4b38e7f","repo":"vercel/next.js","slug":"after-argument-must-be-a-promise-or-a-functio","errorCode":null,"errorMessage":"`after()`: Argument must be a promise or a function","messagePattern":"`after\\(\\)`: Argument must be a promise or a function","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/next/src/server/after/after-context.ts","lineNumber":72,"sourceCode":"\n  public after(task: AfterTask, workUnitStore: WorkUnitStore): void {\n    if (this.initialOnCloseError) {\n      throw new InvariantError(\n        `An onClose call failed, which means after() can't work correctly.`,\n        { cause: this.initialOnCloseError.error }\n      )\n    }\n\n    // Save the workUnitStore so we can switch its phase later.\n    this.workUnitStores.add(workUnitStore)\n\n    if (isThenable(task)) {\n      this.addThenable(task)\n    } else if (typeof task === 'function') {\n      // TODO(after): implement tracing\n      this.addCallback(task, workUnitStore)\n    } else {\n      throw new Error('`after()`: Argument must be a promise or a function')\n    }\n  }\n\n  private addThenable(thenable: PromiseLike<any>) {\n    if (!this.waitUntil) {\n      errorWaitUntilNotAvailable()\n    }\n    this.waitUntil(\n      new Promise<void>((resolve) => {\n        thenable.then(\n          () => {\n            resolve()\n          },\n          (error) => {\n            resolve()\n            this.reportTaskError('promise', error)\n          }\n        )","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/vercel/next.js/blob/0ae8c72462952df163f1b1e0726641bc5b40dc93/packages/next/src/server/after/after-context.ts#L54-L90","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a function: after(() => { /* deferred work */ }).","Or pass a Promise: after(someAsyncTask()).","Add a TypeScript type annotation using AfterTask<T> so the compiler rejects invalid arguments before runtime."],"exampleFix":"// before\nafter(undefined)\nafter({ log: 'done' })\n// after\nafter(() => { console.log('done') })\nafter(sendAnalytics())","handlingStrategy":"type-guard","validationCode":"function validateAfterTask(task: unknown) {\n  if (task !== null && (typeof task === 'function' || (typeof task === 'object' && typeof (task as any).then === 'function'))) return\n  throw new Error('after() requires a Promise or a function')\n}","typeGuard":"import type { AfterTask } from 'next/server'\nfunction isAfterTask(t: unknown): t is AfterTask {\n  return typeof t === 'function' || (t != null && typeof (t as any).then === 'function')\n}","tryCatchPattern":null,"preventionTips":["Type after() arguments with AfterTask<T> so the compiler checks them.","Pass either an arrow function or an already-invoked async function's Promise.","Never call after() with no argument or with a plain object."],"tags":["after","api","runtime","validation"],"analyzedSha":"0ae8c72462952df163f1b1e0726641bc5b40dc93","analyzedAt":"2026-08-06T19:44:29.143Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}