serverless/serverless · error · ServerlessError

INVOKE_LOCAL_UNSUPPORTED_ENV_VARIABLE

INVOKE_LOCAL_UNSUPPORTED_ENV_VARIABLE

Error message

Unsupported environment variable format: ${inspect(value)}

What it means

Thrown by invoke-local's environment resolver when an env-var value is an object (a CloudFormation intrinsic) but uses a key other than Fn::ImportValue or Ref. The local resolver only knows those two intrinsics; anything else (Fn::Sub, Fn::Join, Fn::GetAtt, etc.) is unsupported for local invocation.

Source

Thrown at packages/serverless/lib/plugins/aws/invoke-local/index.js:285

  }

  async resolveConfiguredEnvVars(configuredEnvVars) {
    await Promise.all(
      Object.entries(configuredEnvVars).map(async ([name, value]) => {
        if (!_.isObject(value)) return
        try {
          if (value['Fn::ImportValue']) {
            configuredEnvVars[name] = await resolveCfImportValue(
              this.provider,
              value['Fn::ImportValue'],
            )
          } else if (value.Ref) {
            configuredEnvVars[name] = await resolveCfRefValue(
              this.provider,
              value.Ref,
            )
          } else {
            throw new ServerlessError(
              `Unsupported environment variable format: ${inspect(value)}`,
              'INVOKE_LOCAL_UNSUPPORTED_ENV_VARIABLE',
            )
          }
        } catch (error) {
          throw new ServerlessError(
            `Could not resolve "${name}" environment variable: ${error.message}`,
            'INVOKE_LOCAL_INVALID_ENV_VARIABLE',
          )
        }
      }),
    )

    return configuredEnvVars
  }

  async loadEnvVars() {
    const lambdaName = this.options.functionObj.name

View on GitHub (pinned to b9d7ea51c8)

Solutions

  1. Replace the unsupported intrinsic with Fn::ImportValue or Ref if the underlying value can be expressed that way.
  2. Use a literal string for the value when invoking locally.
  3. Resolve the composed value out-of-band and pass it via a local-only environment file/variable.

Example fix

# before
environment:
  TOPIC_ARN:
    Fn::Sub: 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:my-topic'
# after (resolvable locally)
environment:
  TOPIC_ARN:
    Ref: MyTopicParameter
Defensive patterns

Strategy: type-guard

Validate before calling

const SUPPORTED = ['Fn::ImportValue', 'Ref']
function isResolvableEnvValue(v) {
  if (v == null || typeof v !== 'object') return true
  return SUPPORTED.some((k) => k in v)
}
for (const [k, v] of Object.entries(mergedEnv)) {
  if (!isResolvableEnvValue(v)) throw new Error(`env ${k} uses an unsupported intrinsic for local invoke`)
}

Type guard

function isLocallyResolvableIntrinsic(v) {
  return v == null || typeof v !== 'object' || 'Fn::ImportValue' in v || 'Ref' in v
}

Prevention

When it happens

Trigger: Setting provider.environment or function.environment entries to `{ 'Fn::Sub': '...' }`, `{ 'Fn::Join': [...] }`, `{ 'Fn::GetAtt': [...] }`, or any intrinsic besides ImportValue/Ref, then running `sls invoke local`.

Common situations: Using Fn::Sub/Fn::Join in environment values (valid at deploy time, but the local invoker cannot resolve them); migrating config that relied on intrinsic composition.

Related errors


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