Error codes (R402_*)
Stable error codes returned from Run402 Astro SSR runtime deployments, runtime, SDK, and cache layer. Every code has the same envelope shape:
{ "ok": false, "code": "R402_*", "message": "human description", "suggestedFix": "concrete action the agent/dev should take", "docs": "https://docs.run402.com/<topic>/<page>#<anchor>", "file": "src/pages/[slug].astro", "line": 14, "stage": "snapstart_validate"}file, line, stage, activated, requestId are optional and present when applicable.
Build / deploy time
Section titled “Build / deploy time”R402_ASTRO_BUILD_FAILED
Section titled “R402_ASTRO_BUILD_FAILED”Astro’s own build pipeline threw an unrecovered error. message carries the compiler error summary; file / line when statically determinable.
Suggested fix: check the build log for the specific Astro compiler error and address it (typically a syntax error, type error, or missing import in a .astro page).
R402_ASTRO_UNSUPPORTED_OUTPUT
Section titled “R402_ASTRO_UNSUPPORTED_OUTPUT”output: '...' mode is not supported by the Run402 adapter. v1 supports 'server', 'static', and (deprecated) 'hybrid' only.
Suggested fix: use output: 'server' and opt-in to per-route static prerender via export const prerender = true; in any page that should be pre-built.
R402_ASTRO_MIDDLEWARE_UNSUPPORTED
Section titled “R402_ASTRO_MIDDLEWARE_UNSUPPORTED”Astro middleware at src/middleware.ts is supported by Run402, but a specific middleware pattern hit an implementation snag (typically context-propagation incompatibility). When this code surfaces, the middleware ran but its behavior may diverge from Astro defaults.
Suggested fix: move auth-gating logic from src/middleware.ts into individual page frontmatter OR API endpoint handlers. Use Astro.locals.run402.requestId / cookie reading on the page.
R402_ASTRO_SERVER_ISLAND_UNSUPPORTED
Section titled “R402_ASTRO_SERVER_ISLAND_UNSUPPORTED”Found a server:defer or server:only directive. Server islands are deferred to v1.5.
Suggested fix: use client islands (client:load, client:idle, client:visible) instead, OR move the rendering into the page’s frontmatter at SSR time.
R402_ASTRO_SESSIONS_UNSUPPORTED
Section titled “R402_ASTRO_SESSIONS_UNSUPPORTED”Found Astro.session.* access OR experimental: { session: ... } in astro.config.mjs. Sessions API deferred to v1.5+.
Suggested fix: use signed HTTP-only cookies via Astro.cookies OR a custom DB-backed session via db() to persist session state.
R402_ASTRO_DYNAMIC_IMAGE_UNSUPPORTED
Section titled “R402_ASTRO_DYNAMIC_IMAGE_UNSUPPORTED”Found <Image src={expr}> where expr is not statically resolvable (DB-row property access, function call, environment variable, frontmatter value, etc.).
Suggested fix: for CMS images, store the full AssetRef JSON from assets.put() at upload time and render with <Run402Picture asset={page.hero_asset} />. For build-time static images, use <Image src="./hero.jpg"> with a string literal OR import hero from "../assets/hero.png"; <Image src={hero} /> with a static import binding.
R402_ASTRO_VERSION_UNSUPPORTED
Section titled “R402_ASTRO_VERSION_UNSUPPORTED”Installed astro version is outside the Run402 adapter’s pinned peer-range.
Suggested fix: check package.json and pin Astro to ^6.0.0 (or the range documented in @run402/astro’s peerDependencies). Run npm install astro@^6.
R402_BUNDLE_UNRESOLVED_IMPORT
Section titled “R402_BUNDLE_UNRESOLVED_IMPORT”The deploy bundler couldn’t resolve an import in a function file.
Suggested fix: check the import path is correct; ensure the package is in package.json’s dependencies (NOT devDependencies); run npm install.
R402_BUNDLE_NATIVE_DEP_UNSUPPORTED
Section titled “R402_BUNDLE_NATIVE_DEP_UNSUPPORTED”The function bundle includes a package with native binary components (e.g., sharp, better-sqlite3, node-gyp-built packages). Lambda’s Node.js runtime cannot load arbitrary native binaries.
Suggested fix: remove the native dependency OR replace with a Run402 primitive — r.assets.put for image processing (returns the v1.49 variant ladder + blurhash + display URLs), r.ai.* for ML, r.email.send for emails, db() for SQLite-style storage.
SnapStart
Section titled “SnapStart”R402_SNAPSTART_INIT_IO
Section titled “R402_SNAPSTART_INIT_IO”The SSR-class function did network/database IO at module scope, which SnapStart cannot snapshot consistently. Three places this surfaces:
- Deploy-time pre-activation validation —
waitUntilPublishedVersionActivefailed to confirm a clean snapshot. The deploy still activates (function works without SnapStart’s optimization), but the cold-start latency is the legacy ~600ms instead of the SnapStart ~120ms. - Runtime guard in
@run402/functions— module-scope IO is intercepted and throws this code during snapshot init. - Build-time static scan — best-effort heuristic flags top-level
awaiton known network primitives as a warning (not failure).
Suggested fix: move network/database calls from module scope INSIDE the request handler (Astro frontmatter or APIRoute body). Example:
// Wrong:const settings = await db().from('settings').select('*');export default async () => { /* uses settings */ };
// Right:let settings: Setting[] | undefined;export default async () => { if (!settings) settings = await db().from('settings').select('*'); /* uses settings */};R402_SDK_OUTSIDE_REQUEST_CONTEXT
Section titled “R402_SDK_OUTSIDE_REQUEST_CONTEXT”An SDK function (db, auth.*, cache.*, etc.) was called outside an active request context. Two common causes:
- Module scope — calling an SDK function at the top level of a module file rather than inside a handler.
- Background timer / unawaited promise —
setTimeout(() => db()..., 60000)scheduled inside a handler, fires AFTER the response was materialized. The runtime context’sactive.valueis set tofalsepost-response so the timer’s SDK call throws.
Suggested fix: move the SDK call inside an HTTP request handler. Don’t schedule background work that outlives the response — use scheduled functions (schedule: '0 */6 * * *') for periodic tasks instead.
R402_SSR_RUNTIME_ERROR
Section titled “R402_SSR_RUNTIME_ERROR”The SSR Lambda function threw an uncaught exception during render. The response body contains the structured envelope (without stack trace). The full stack is logged to the request’s log stream — retrieve via run402 logs --request-id <req_xyz>.
Suggested fix:
run402 logs --request-id <req>to fetch the full stack trace.- Fix the exception in the source (typically a null-deref, missing env var, or unhandled rejection).
- Re-deploy.
Cache layer
Section titled “Cache layer”R402_CACHE_UNSUPPORTED_VARY
Section titled “R402_CACHE_UNSUPPORTED_VARY”A response has a Vary header referencing something other than Accept-Language (which is already encoded in the cache key as locale). The response is delivered normally to the client but is NOT cached. The bypass reason is also emitted via x-run402-cache-reason: unsupported_vary.
Suggested fix: remove the Vary header OR restructure the route so different variants live at different paths.
R402_CACHE_AUTH_TAINTED
Section titled “R402_CACHE_AUTH_TAINTED”Informational diagnostic (NOT an ok: false error). The cache layer detected that the render called auth.user() (or another auth.* helper) or a payment primitive and bypassed storage with x-run402-cache-reason: auth. This is the expected behavior — auth-dependent renders are uncacheable by design.
Suggested fix: none — this is correct behavior. If you want a page to be cacheable, ensure it doesn’t call auth.* helpers (use Astro.cookies directly if you need to check for cookie presence without flipping the taint flag — but consider whether that page should be cached at all).
R402_CACHE_INVALIDATION_HOST_REQUIRED
Section titled “R402_CACHE_INVALIDATION_HOST_REQUIRED”cache.invalidate('/path') (path-string form) was called outside a request context. The path-string form needs the current host from the ALS request context to scope correctly.
Suggested fix: use the absolute-URL form: cache.invalidate(new URL('https://eagles.kychon.com/the-guys')) OR cache.invalidate('https://eagles.kychon.com/the-guys'). Or move the call into a request handler.
R402_CACHE_INVALIDATION_HOST_FORBIDDEN
Section titled “R402_CACHE_INVALIDATION_HOST_FORBIDDEN”cache.invalidate(<absolute URL>) targeted a host that is NOT owned by the caller’s authenticated project. Cross-project cache mutation is rejected for tenant isolation.
Suggested fix: use a host attached to your project. Run run402 domains list to see your project’s attached hosts.
Deploy
Section titled “Deploy”R402_DEPLOY_STAGE_FAILED
Section titled “R402_DEPLOY_STAGE_FAILED”The deploy pipeline failed at a specific stage of the multi-slice apply-v1 state machine. stage carries the stage name (validate, stage, migrate, schema_settling, activating, snapstart_validate, etc.).
Suggested fix: check the stage and the message. Common causes:
migrate— SQL migration failed; fix the migration file or use the operator escape hatch.schema_settling— PostgREST didn’t pick up the schema within the budget; usually transient, retry.activating— atomic activation failed; usually a constraint violation; check the underlying error.snapstart_validate— seeR402_SNAPSTART_INIT_IOabove.
Docs URL convention
Section titled “Docs URL convention”All docs URLs follow the pattern https://docs.run402.com/<topic>/<page>#<anchor> where:
<topic>∈astro,functions,cache,deploy,cli,sdk,assets<page>is a stable page name within the topic (errors,images,concepts)<anchor>matches a documented heading within the page
URLs are stable across releases. Anchors are not removed or renamed without a redirect.