{"record":{"id":"99309e1661fa50fa","repo":"withastro/astro","slug":"fetchstate-not-found-on-apicontext-next-payload","errorCode":null,"errorMessage":"FetchState not found on APIContext. `next(payload)` rewrites require a context created through Astro's request pipeline.","messagePattern":"FetchState not found on APIContext\\. `next\\(payload\\)` rewrites require a context created through Astro's request pipeline\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/core/middleware/sequence.ts","lineNumber":47,"sourceCode":"\t\t/**\n\t\t * This variable is used to carry the rerouting payload across middleware functions.\n\t\t */\n\t\tlet carriedPayload: RewritePayload | undefined = undefined;\n\t\treturn applyHandle(0, context);\n\n\t\tfunction applyHandle(i: number, handleContext: APIContext) {\n\t\t\tconst handle = filtered[i];\n\t\t\t// @ts-expect-error\n\t\t\t// SAFETY: Usually `next` always returns something in user land, but in `sequence` we are actually\n\t\t\t// doing a loop over all the `next` functions, and eventually we call the last `next` that returns the `Response`.\n\t\t\tconst result = handle(handleContext, async (payload?: RewritePayload) => {\n\t\t\t\tif (i < length - 1) {\n\t\t\t\t\tif (payload) {\n\t\t\t\t\t\tconst oldPathname = handleContext.url.pathname;\n\t\t\t\t\t\tconst state = Reflect.get(handleContext, fetchStateSymbol) as FetchState | undefined;\n\t\t\t\t\t\tif (!state) {\n\t\t\t\t\t\t\t// Outside Astro's request pipeline the state is never stamped.\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\"FetchState not found on APIContext. `next(payload)` rewrites require a context created through Astro's request pipeline.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst manifest = state.manifest;\n\t\t\t\t\t\tconst { routeData, pathname } = await getEnvironment(manifest).tryRewrite(\n\t\t\t\t\t\t\tmanifest,\n\t\t\t\t\t\t\tpayload,\n\t\t\t\t\t\t\thandleContext.request,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tlet newRequest: Request;\n\t\t\t\t\t\tif (payload instanceof Request) {\n\t\t\t\t\t\t\tnewRequest = payload;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst request =\n\t\t\t\t\t\t\t\thandleContext.request.method === 'GET' || handleContext.request.method === 'HEAD'\n\t\t\t\t\t\t\t\t\t? handleContext.request\n\t\t\t\t\t\t\t\t\t: handleContext.request.clone();\n\t\t\t\t\t\t\tconst newUrl =","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/withastro/astro/blob/157c500c38faa7ecf1251adbaeefcd109470d75c/packages/astro/src/core/middleware/sequence.ts#L29-L65","documentation":"sequence()'s rewrite form — next('/path') — retrieves the FetchState that Astro's request pipeline stamps on the context under a private symbol. Contexts created any other way (notably createContext() contexts used by adapter edge-middleware bridges) never carry that symbol, so the rewrite cannot be routed and this Error is thrown: the context must come through Astro's request pipeline for payload rewrites.","triggerScenarios":"Middleware running under an adapter's own context (Vercel/Netlify edge middleware chain) calls next('/some-route'); invoking sequence() manually in tests or a custom host with a fabricated context and using the rewrite payload form.","commonSituations":"Moving server-side middleware that uses next(payload) rewrites to the edge; adapters that run the middleware chain outside app.render; unit tests driving sequence() with a hand-built context.","solutions":["In edge middleware, use the platform's native rewrite mechanism instead of next('/path')","Return a redirect Response (context.redirect(target)) instead of rewriting — redirects do not need the pipeline state","Perform Astro rewrites from server-rendered pipeline middleware, not from adapter-bridged contexts","In tests, exercise the full pipeline (app.render) rather than calling sequence() with a bare context"],"exampleFix":"// before (edge middleware)\nreturn next('/dashboard');\n\n// after\nreturn context.redirect('/dashboard', 302);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return next('/dashboard');\n} catch (err) {\n  if (err instanceof Error && err.message.includes('FetchState not found on APIContext')) {\n    // edge-bridged context — fall back to a redirect, which needs no pipeline state\n    return context.redirect('/dashboard', 302);\n  }\n  throw err;\n}","preventionTips":["In adapter edge middleware, use the platform's native rewrite instead of next('/path')","Reserve Astro payload rewrites for middleware running inside the server pipeline","Test middleware under the same context type the adapter will actually use"],"tags":["middleware","rewrite","next-payload","fetch-state","edge-middleware"],"backgroundTag":"missing-framework-context","analyzedSha":"157c500c38faa7ecf1251adbaeefcd109470d75c","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}