Skip to content

eas deploy --prod reports "Promoted deployment to production" and exits 0 while production keeps serving the previous bundle #4388

Description

@Samuelgray1987

Summary

eas deploy --prod prints ✔ Promoted deployment to production and exits 0 while production continues to serve the previous bundle. The upload half succeeds; the promotion half silently does not. Re-running the identical command promotes it.

This began as an occasional failure and now reproduces on every deploy of this project — four deploys on 2026-09-11, all four needed two runs.

Managed or bare?

Managed. Expo Router web export (static output) on EAS Hosting.

Environment

  expo-env-info 2.1.0 environment info:
    System:
      OS: macOS 26.6.1
    Binaries:
      Node: 24.18.0
      npm: 11.16.0
    npmPackages:
      expo: ~57.0.8 => 57.0.8
      expo-router: ~57.0.8 => 57.0.8
      react: 19.2.3 => 19.2.3
      react-dom: 19.2.3 => 19.2.3
      react-native: 0.86.0 => 0.86.0
      react-native-web: ~0.21.0 => 0.21.2
    Expo Workflow: managed

  eas-cli/24.3.0 darwin-arm64 node-v24.18.0

Error output

There is no error output. That is the substance of the report — the command claims to have done something it has not done:

✔ Created deployment
- Promoting deployment to production
✔ Promoted deployment to production

🎉 Your deployment is ready

Deployment URL  https://warp-nexus--y3iw49p3qz.expo.app
Production URL  https://warp-nexus.expo.app

Exit code 0. Production continued serving the prior build.

Reproducible demo or steps to reproduce from a blank project

Each build stamps its entry-bundle content hash into sw.js as const VERSION = "<hash>", which lets the live origin be asked which build it is actually serving. Any per-build marker in a static asset works for reproducing this — the point is to compare the deployment-specific URL against the production URL.

npx expo export --platform web
# note the hash of dist/_expo/static/js/web/entry-<hash>.js
npx eas-cli deploy --prod --export-dir dist --non-interactive
# command prints "Promoted deployment to production" and exits 0

curl -s "https://warp-nexus.expo.app/sw.js"              | grep VERSION   # OLD build
curl -s "https://warp-nexus--<deployId>.expo.app/sw.js"  | grep VERSION   # NEW build

Measured immediately after a run that claimed to have promoted:

Origin VERSION served
warp-nexus--y3iw49p3qz.expo.app (that run's own deployment URL) 7067f55ae830 ← the build just made
warp-nexus.expo.app (production) c0547b2ed28a ← the previous build

The new deployment exists and is complete at its own URL, so the export and upload are fine. Production was simply not moved to it.

Re-running the same command produced deployment m9fb2fyn8e; production then served 7067f55ae830.

The same sequence repeated later that day with a different build: production stayed on 7067f55ae830 after a "successful" promotion, and moved to c09a2c8b7e8e only after a second identical run.

Not a propagation delay. Production is polled 6 times over 30 seconds before being called a failure, and stays on the old bundle throughout. After the second run it is correct within 10 seconds.

Expected behaviour

Either the promotion happens, or the command reports that it did not and exits non-zero. A zero exit code printed alongside ✔ Promoted deployment to production should not be compatible with production serving a different bundle.

Impact

Without an external check, a deploy that appears to have succeeded leaves users on the old build indefinitely, with nothing anywhere to indicate it. We now gate every deploy on fetching sw.js from the production origin and comparing its hash against the local export, failing the deploy when they differ. That works, but it is a workaround for a command reporting an action it did not perform.

This is the same shape as #4185 (EAS Workflows reporting SKIPPED jobs as a success commit status): the failure mode is not that something broke, but that the tooling asserted success for work that did not happen.

Happy to supply project and deployment IDs privately if that helps trace the promotion calls server-side.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions