serverless/serverless · error · ServerlessError

EXTERNAL_HTTP_API_CORS_CONFIG

EXTERNAL_HTTP_API_CORS_CONFIG

Error message

Cannot setup CORS rules for externally configured HTTP API

What it means

Thrown by the HTTP API (API Gateway v2) event plugin during configuration resolution. provider.httpApi.id marks the API as externally managed (created outside this stack), so the framework cannot attach or modify CORS settings on it. Setting provider.httpApi.cors alongside provider.httpApi.id is therefore rejected. CORS must be configured on the external API directly (AWS console, separate stack, or CDK).

Source

Thrown at packages/serverless/lib/plugins/aws/package/compile/events/http-api.js:456

Object.defineProperties(
  HttpApiEvents.prototype,
  memoizeeMethods({
    resolveConfiguration: d(function () {
      const routes = new Map()
      const providerConfig = this.serverless.service.provider
      const userConfig = providerConfig.httpApi || {}
      this.config = {
        routes,
        id: userConfig.id,
        metrics: userConfig.metrics || false,
        disableDefaultEndpoint: userConfig.disableDefaultEndpoint,
      }
      let cors = null
      let shouldFillCorsMethods = false
      const userCors = userConfig.cors
      if (userCors) {
        if (userConfig.id) {
          throw new ServerlessError(
            'Cannot setup CORS rules for externally configured HTTP API',
            'EXTERNAL_HTTP_API_CORS_CONFIG',
          )
        }
        cors = this.config.cors = {}
        if (userConfig.cors === true) {
          Object.assign(cors, defaultCors)
          shouldFillCorsMethods = true
        } else {
          cors.allowedOrigins = userCors.allowedOrigins
            ? toSet(userCors.allowedOrigins)
            : defaultCors.allowedOrigins
          cors.allowedHeaders = userCors.allowedHeaders
            ? toSet(userCors.allowedHeaders)
            : defaultCors.allowedHeaders
          if (userCors.allowedMethods)
            cors.allowedMethods = toSet(userCors.allowedMethods)
          else shouldFillCorsMethods = true

View on GitHub (pinned to b9d7ea51c8)

Solutions

  1. Remove the provider.httpApi.cors block from serverless.yml and configure CORS on the external HTTP API in its owning stack/CDK/Console.
  2. If you want the framework to manage CORS, remove provider.httpApi.id so the API is created in this stack.
  3. Double-check no computed value (variable/resolver) is silently producing a truthy cors when id is also set.

Example fix

# before
provider:
  httpApi:
    id: abc123xyz
    cors: true
# after
provider:
  httpApi:
    id: abc123xyz
# (configure CORS on the external API itself)
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight check before deploy/package
import { readFileSync } from 'fs'
const cfg = require('js-yaml').load(readFileSync('serverless.yml','utf8'))
const h = cfg.provider?.httpApi ?? {}
if (h.id && h.cors !== undefined) {
  throw new Error('EXTERNAL_HTTP_API_CORS_CONFIG: remove httpApi.cors when httpApi.id is set')
}

Type guard

// True when the API is externally managed and CORS must NOT be set
const isExternalHttpApiWithCorsConflict = (httpApi) =>
  Boolean(httpApi?.id) && httpApi?.cors !== undefined && httpApi?.cors !== null

Prevention

When it happens

Trigger: In serverless.yml, provider.httpApi.id is set to a string/CFN reference (e.g. an existing API ID) AND provider.httpApi.cors is truthy (either true, or an object with allowedOrigins etc.). The resolveConfiguration hook runs during package:compileEvents and the very first cors branch throws.

Common situations: Migrating an HTTP API to be externally managed (importing an existing API) while leaving the previously-working cors block in place. Copying a service config snippet that includes cors without noticing the id field is set. Splitting a monolith into a shared-API stack + function stacks.

Related errors


AI-assisted analysis of serverless/serverless@b9d7ea51c8 (2026-08-13). Data as JSON: /api/errors/f9d031894a5d9211. Report an issue: GitHub.