Import.meta.env breaks Astro middleware build

Site name: meek-strudel-8f66a0.netlify.app

I’m building an Astro site. I’m using middleware on the edge runtime.

If my middleware includes import.meta.env, then the build fails.

// src/middleware.ts
import type { MiddlewareHandler } from 'astro' // this fails
const NODE_ENV = import.meta.env.NODE_ENV
export const onRequest: MiddlewareHandler = async (context, next) => {
  const response = await next()
  response.set('NODE_ENV', NODE_ENV)
  return response
}

If I move the import.meta.env out of the static scope into the handler, it passes, though the variable is then undefined in the middleware even though it’s set during the build phase.

Full build log, with import.meta.env.NODE_ENV in the static module scope:

8:06:00 PM: Netlify Build                                                 
8:06:00 PM: ────────────────────────────────────────────────────────────────
8:06:00 PM: ​
8:06:00 PM: ❯ Version
8:06:00 PM:   @netlify/build 29.41.1
8:06:00 PM: ​
8:06:00 PM: ❯ Flags
8:06:00 PM:   apiHost: api.netlify.com
8:06:00 PM:   baseRelDir: true
8:06:00 PM:   branch: james/upgrade-astro
8:06:00 PM:   buildId: 6636f776d617d9000873f329
8:06:00 PM:   buildbotServerSocket: /tmp/netlify-buildbot-socket
8:06:00 PM:   cacheDir: /opt/build/cache
8:06:00 PM:   cachedConfigPath: /tmp/netlify_config.json
8:06:00 PM:   context: deploy-preview
8:06:00 PM:   cwd: /opt/build/repo
8:06:00 PM:   deployId: 6636f776d617d9000873f32b
8:06:00 PM:   edgeFunctionsDistDir: /tmp/edge-6636f776d617d9000873f32b
8:06:00 PM:   featureFlags:
8:06:00 PM:     - buildbot_zisi_system_log
8:06:00 PM:     - edge_functions_cache_cli
8:06:00 PM:     - edge_functions_system_logger
8:06:00 PM:     - netlify_build_updated_plugin_compatibility
8:06:00 PM:   framework: astro
8:06:00 PM:   functionsDistDir: /tmp/zisi-6636f776d617d9000873f32b
8:06:00 PM:   mode: buildbot
8:06:00 PM:   nodePath: /opt/buildhome/.nvm/versions/node/v18.20.2/bin/node
8:06:00 PM:   repositoryRoot: /opt/build/repo
8:06:00 PM:   saveConfig: true
8:06:00 PM:   sendStatus: true
8:06:00 PM:   siteId: ee5bdbb1-ad9d-426f-81bc-a35701d49774
8:06:00 PM:   statsd:
8:06:00 PM:     host: 10.71.77.198
8:06:00 PM:     port: 8125
8:06:00 PM:   systemLogFile: 3
8:06:00 PM:   testOpts:
8:06:00 PM:     silentLingeringProcesses: ''
8:06:00 PM:   tracing:
8:06:00 PM:     baggageFilePath: /tmp/baggage.dump
8:06:00 PM:     enabled: 'true'
8:06:00 PM:     host: 10.71.77.198
8:06:00 PM:     parentSpanId: 1a6ec6b7ff787a3f
8:06:00 PM:     preloadingEnabled: 'true'
8:06:00 PM:     sampleRate: 4
8:06:00 PM:     traceFlags: '00'
8:06:00 PM:     traceId: 942a577e4c6272f4a698a2e040bb0106
8:06:00 PM: ​
8:06:00 PM: ❯ Current directory
8:06:00 PM:   /opt/build/repo
8:06:00 PM: ​
8:06:00 PM: ❯ Config file
8:06:00 PM:   /opt/build/repo/netlify.toml
8:06:00 PM: ​
8:06:00 PM: ❯ Resolved config
8:06:00 PM:   build:
8:06:00 PM:     command: CI= pnpm run build
8:06:00 PM:     commandOrigin: config
8:06:00 PM:     environment:
8:06:00 PM:       - CSP_REPORT_URI
8:06:00 PM:       - NETLIFY_BUILD_DEBUG
8:06:00 PM:       - NETLIFY_CONTEXT
8:06:00 PM:       - NODE_VERSION
8:06:00 PM:       - REVIEW_ID
8:06:00 PM:       - SCHEDULER_URL
8:06:00 PM:       - SENTRY_DEBUG
8:06:00 PM:       - SENTRY_ENABLED
8:06:00 PM:       - SENTRY_SAMPLE_RATE
8:06:00 PM:     publish: /opt/build/repo/dist
8:06:00 PM:     publishOrigin: config
8:06:00 PM: ​
8:06:00 PM: ❯ Context
8:06:00 PM:   deploy-preview
8:06:00 PM: ​
8:06:00 PM: build.command from netlify.toml                               
8:06:00 PM: ────────────────────────────────────────────────────────────────
8:06:00 PM: ​
8:06:00 PM: $ CI= pnpm run build
8:06:00 PM: > jamesarosen.com@0.0.1 build /opt/build/repo
8:06:00 PM: > pnpm run build:astro && pnpm run build:netlify
8:06:01 PM: > jamesarosen.com@0.0.1 build:astro /opt/build/repo
8:06:01 PM: > NODE_ENV=${NODE_ENV:-production}; astro build
8:06:02 PM: 03:06:02 [WARN] [config] The feature "assets" is experimental and subject to change (used by @astrojs/netlify).
8:06:02 PM: 03:06:02 [build] output: "server"
8:06:02 PM: 03:06:02 [build] directory: /opt/build/repo/dist/
8:06:02 PM: 03:06:02 [build] adapter: @astrojs/netlify
8:06:02 PM: 03:06:02 [build] Collecting build info...
8:06:02 PM: 03:06:02 [build] ✓ Completed in 67ms.
8:06:02 PM: 03:06:02 [build] Building server entrypoints...
8:06:03 PM: 03:06:03 [vite] ✓ built in 807ms
8:06:03 PM: 03:06:03 [build] ✓ Completed in 836ms.
8:06:03 PM:  building client (vite) 
8:06:03 PM: 03:06:03 [vite] transforming...
8:06:03 PM: 03:06:03 [vite] ✓ 194 modules transformed.
8:06:03 PM: 03:06:03 [vite] rendering chunks...
8:06:03 PM: 03:06:03 [vite] computing gzip size...
8:06:03 PM: 03:06:03 [vite] dist/_astro/hoisted.CR2htZYs.js  117.42 kB │ gzip: 38.60 kB
8:06:03 PM: 03:06:03 [vite] ✓ built in 717ms
8:06:03 PM: {
8:06:03 PM:   'import.meta': [Object: null prototype] {
8:06:03 PM:     resolve: [Function: resolve],
8:06:03 PM:     url: 'file:///opt/build/repo/.netlify/functions-internal/ssr/chunks/pages/hire_0CYeykqJ.mjs'
8:06:03 PM:   },
8:06:03 PM:   'import.meta.env': {
8:06:03 PM:     BASE_URL: '/',
8:06:03 PM:     MODE: 'production',
8:06:03 PM:     DEV: false,
8:06:03 PM:     PROD: true,
8:06:03 PM:     SSR: true,
8:06:03 PM:     SITE: undefined,
8:06:03 PM:     ASSETS_PREFIX: undefined,
8:06:03 PM:     NETLIFY_CONTEXT: 'deploy-preview',
8:06:03 PM:     _: '/opt/buildhome/.nvm/versions/node/v18.20.2/bin/pnpm',
8:06:03 PM:     NODE: '/opt/buildhome/.nvm/versions/node/v18.20.2/bin/node',
8:06:03 PM:     CONTEXT: 'deploy-preview',
8:06:03 PM:     HEAD: 'james/upgrade-astro',
8:06:03 PM:     CSP_REPORT_URI: 'REDACTED',
8:06:03 PM:     NETLIFY: true,
8:06:03 PM:     NODE_ENV: 'production'
8:06:03 PM:   },
8:06:03 PM:   'import.meta.env.CSP_REPORT_URI': 'REDACTED',
8:06:03 PM:   'import.meta.env.DEV': false,
8:06:03 PM:   'import.meta.env.NETLIFY_CONTEXT': 'deploy-preview',
8:06:03 PM:   'typeof process': 'object',
8:06:03 PM:   'process.env.NODE_ENV': 'production'
8:06:03 PM: }
8:06:03 PM: 03:06:03
8:06:03 PM:  finalizing server assets 
8:06:03 PM: 03:06:03 [build] Rearranging server assets...
8:06:03 PM: 03:06:03 [@astrojs/netlify] Emitted _redirects
8:06:03 PM: 03:06:03 [@astrojs/netlify] Generated SSR Function
8:06:03 PM: 03:06:03 [@astrojs/netlify] Generated Middleware Edge Function
8:06:03 PM: 03:06:03 [build] Server built in 1.67s
8:06:03 PM: 03:06:03 [build] Complete!
8:06:04 PM: > jamesarosen.com@0.0.1 build:netlify /opt/build/repo
8:06:04 PM: > cp _headers dist/
8:06:04 PM: ​
8:06:04 PM: ❯ Updated config
8:06:04 PM:   build:
8:06:04 PM:     command: CI= pnpm run build
8:06:04 PM:     commandOrigin: config
8:06:04 PM:     environment:
8:06:04 PM:       - CSP_REPORT_URI
8:06:04 PM:       - NETLIFY_BUILD_DEBUG
8:06:04 PM:       - NETLIFY_CONTEXT
8:06:04 PM:       - NODE_VERSION
8:06:04 PM:       - REVIEW_ID
8:06:04 PM:       - SCHEDULER_URL
8:06:04 PM:       - SENTRY_DEBUG
8:06:04 PM:       - SENTRY_ENABLED
8:06:04 PM:       - SENTRY_SAMPLE_RATE
8:06:04 PM:     publish: /opt/build/repo/dist
8:06:04 PM:     publishOrigin: config
8:06:04 PM:   headers:
8:06:04 PM:     - for: /_astro/*.*.*
      values:
        cache-control: public, max-age=86400, stale-while-revalidate=2592000, stale-if-error=2592000, immutable
​
8:06:04 PM: (build.command completed in 3.7s)
8:06:04 PM: Build step duration: build.command completed in 3762ms
8:06:04 PM: ​
8:06:04 PM: Functions bundling                                            
8:06:04 PM: ────────────────────────────────────────────────────────────────
8:06:04 PM: ​
8:06:04 PM: Packaging Functions from .netlify/functions-internal directory:
8:06:04 PM:  - ssr/ssr.mjs
8:06:04 PM: ​
8:06:05 PM: ​
8:06:05 PM: (Functions bundling completed in 724ms)
8:06:05 PM: Build step duration: Functions bundling completed in 724ms
8:06:05 PM: ​
8:06:05 PM: Edge Functions bundling                                       
8:06:05 PM: ────────────────────────────────────────────────────────────────
8:06:05 PM: ​
8:06:05 PM: Packaging Edge Functions from .netlify/edge-functions directory:
8:06:05 PM:  - middleware
8:06:05 PM: Local version of types is up-to-date: 663522976e5ecd0008f2a7e3
8:06:05 PM: Using global installation of Deno CLI
8:06:05 PM: Using global installation of Deno CLI
8:06:05 PM: TypeError: Cannot read properties of undefined (reading 'env')
8:06:05 PM:     at file:///opt/build/repo/.netlify/edge-functions/middleware/middleware.mjs:1689:188
8:06:05 PM: ​
8:06:05 PM: Bundling of edge function failed                              
8:06:05 PM: ────────────────────────────────────────────────────────────────
8:06:05 PM: ​
8:06:05 PM:   Error message
8:06:05 PM:   Could not load edge function at '/opt/build/repo/.netlify/edge-functions/middleware/middleware.mjs'. More on the Edge Functions API at https://ntl.fyi/edge-api.
8:06:05 PM: ​
8:06:05 PM:   Error location
8:06:05 PM:   While bundling edge function
8:06:05 PM: ​
8:06:05 PM:   Resolved config
8:06:05 PM:   build:
8:06:05 PM:     command: CI= pnpm run build
8:06:05 PM:     commandOrigin: config
8:06:05 PM:     environment:
8:06:05 PM:       - CSP_REPORT_URI
8:06:05 PM:       - NETLIFY_BUILD_DEBUG
8:06:05 PM:       - NETLIFY_CONTEXT
8:06:05 PM:       - NODE_VERSION
8:06:05 PM:       - REVIEW_ID
8:06:05 PM:       - SCHEDULER_URL
8:06:05 PM:       - SENTRY_DEBUG
8:06:05 PM:       - SENTRY_ENABLED
8:06:05 PM:       - SENTRY_SAMPLE_RATE
8:06:05 PM:     publish: /opt/build/repo/dist
8:06:05 PM:     publishOrigin: config
8:06:05 PM:   headers:
8:06:06 PM: Failed during stage 'building site': Build script returned non-zero exit code: 2 (https://ntl.fyi/exit-code-2)
8:06:06 PM:     - for: /_astro/*.*.*
      values:
        cache-control: public, max-age=86400, stale-while-revalidate=2592000, stale-if-error=2592000, immutable
8:06:06 PM: Build failed due to a user error: Build script returned non-zero exit code: 2
8:06:06 PM: Failing build: Failed to build site
8:06:06 PM: Finished processing build request in 27.139s

Oddly, Astro compiles this

console.log('import.meta.env.NODE_ENV', import.meta?.env?.NODE_ENV)

to this:

console.log("import.meta.env.NODE_ENV", process.env.NODE_ENV);

Changing import.meta?.env?.NODE_ENV to process?.env?.NODE_ENV leaves the null-coalescing operators intact. This lets the build pass, but the value is undefined whether I evaluate it in the global module scope or at runtime.

I found a related Astro compiler bug: astro build "infects" middleware with component runtime · Issue #10952 · withastro/astro · GitHub

By adding a shared library, the Astro compiler “infects” my middleware with the Astro component runtime, which isn’t compatible with Netlify’s Edge Runtime.

But that’s not the only way of causing process.env to appear in my Middleware. Netlify seems to have a problem with the Bundling of edge function step if there’s any process.env.FOO in the .netlify/edge-functions/middleware/middleware.mjs file.

I reduced my middleware to this:

import type { MiddlewareHandler } from 'astro'

const NODE_ENV = import.meta.env.NODE_ENV

export const onRequest: MiddlewareHandler = async (context, next) => {
	const response = await next()
	response.headers.set('middleware', 'true')
	response.headers.set('NODE_ENV', NODE_ENV)
	return response
}

and got this during the build:

7:23:50 AM: > grep -C 5 -n -e "process.env" .netlify/edge-functions/middleware/middleware.mjs
7:23:50 AM: 172-    }
7:23:50 AM: 173-  }
7:23:50 AM: 174-});
7:23:50 AM: 175-
7:23:50 AM: 176-// .netlify/functions-internal/ssr/_astro-internal_middleware.mjs
7:23:50 AM: 177:var NODE_ENV = process.env.NODE_ENV;
7:23:50 AM: 178-var onRequest$1 = async (context, next) => {
7:23:50 AM: 179-  const response = await next();
7:23:50 AM: 180-  response.headers.set("middleware", "true");
7:23:50 AM: 181-  response.headers.set("NODE_ENV", NODE_ENV);
7:23:50 AM: 182-  return response;
... snip
7:23:51 AM: Packaging Edge Functions from .netlify/edge-functions directory:
7:23:51 AM:  - middleware
7:23:51 AM: Local version of types is up-to-date: 663522976e5ecd0008f2a7e3
7:23:51 AM: Using global installation of Deno CLI
7:23:51 AM: Using global installation of Deno CLI
7:23:51 AM: TypeError: Cannot read properties of undefined (reading 'env')
7:23:51 AM:     at file:///opt/build/repo/.netlify/edge-functions/middleware/middleware.mjs:177:24
7:23:51 AM: ​
7:23:51 AM: Bundling of edge function failed                              
7:23:51 AM: ────────────────────────────────────────────────────────────────
7:23:51 AM: ​
7:23:51 AM:   Error message
7:23:51 AM:   Could not load edge function at '/opt/build/repo/.netlify/edge-functions/middleware/middleware.mjs'. More on the Edge Functions API at https://ntl.fyi/edge-api.

According to the Netlify Edge Functions docs, that should probably be Netlify.env.get. Perhaps that’s a bug in the Netlify-Astro adapter.

It seems to work for me: hrishikesh-k/f-117809 (github.com) deployed here: Edge Function Invocation Failed (6637b792a100830008e8b717–f-117809.netlify.app)

The only major difference is that I have used import.meta.env inside the onRequest() function. However, it fails during execution so it’s not really useful.

I’ve filed this for the devs to check if this is something we should consider supporting, or something that Astro might have to change in their adapter.

Hi! This is fixed in @astrojs/netlify@5.2.1