Skip to content
Latchkey

GitHub Actions Workflow env Not Inherited by a Reusable / Called Workflow

A variable set in a caller workflow env block is empty inside the reusable workflow it calls. env does not propagate across the workflow_call boundary - only declared inputs and secrets do.

What this error means

A reusable workflow reads an env var the caller defined and gets an empty value, even though the same env block works for the caller’s own jobs.

.github/workflows/ci.yml
# caller
env:
  APP_ENV: staging
jobs:
  call:
    uses: ./.github/workflows/build.yml
# build.yml reads ${{ env.APP_ENV }} -> empty, not "staging"

Common causes

env does not cross workflow_call

A reusable workflow gets a fresh context. The caller’s workflow- and job-level env are not visible inside it; only inputs and secrets are passed explicitly.

Relying on implicit inheritance

Unlike a composite action that runs in the caller’s job, a called workflow runs as its own jobs, so there is no shared env to inherit.

How to fix it

Pass values as declared inputs

Declare an input on the reusable workflow and pass the env value through with:.

.github/workflows/ci.yml
# caller
jobs:
  call:
    uses: ./.github/workflows/build.yml
    with:
      app_env: ${{ env.APP_ENV }}
# build.yml
on:
  workflow_call:
    inputs:
      app_env: { type: string, required: true }

Set env inside the reusable workflow when static

  1. For constant values, define env directly in the reusable workflow.
  2. For caller-specific values, always pass them as inputs.
  3. Pass secrets explicitly or with secrets: inherit - env is never inherited.

How to prevent it

  • Pass dynamic values to reusable workflows as inputs, not via env.
  • Document each reusable workflow’s required inputs and secrets.
  • Do not assume env crosses the workflow_call boundary.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →