actions/deploy-pages deployment failed on the github-pages environment
An actions/deploy-pages deployment failed at one of three gates, and the line in the log tells you which. Two of them reject the ref you are deploying from and one of them rejects what you uploaded, so the fix is a repository setting in two cases out of three and a rebuild in the third.

What this error means
The build job is green and the deploy job is not. Which gate stopped it is visible from how much of the deploy job ran. No steps at all, with the failure as an annotation on the job itself, means the environment refused the ref before the job reached a runner. A deploy step that ran and failed while creating the deployment means the Pages API refused the call, and the action quotes its response. A step that created the deployment and then failed while polling means the deployment reached a terminal state, and the action prints one fixed sentence for it. Three gates, three repairs, and the first leaves no step log at all, which is why it gets mistaken for a broken action.
Branch "v1.1.2" is not allowed to deploy to github-pages due to environment protection rules.Gate one: the environment refuses the ref
A job with environment: github-pages is checked against that environment before it is scheduled. GitHub's reference is unambiguous about the ordering: "All deployment protection rules must pass before a job referencing the environment is sent to a runner." When the deployment branch and tag rule does not match, the job fails as an annotation with the line quoted above and no step ever runs. Nothing in actions/deploy-pages produced that message, so reading its source or bumping its version will not move it.
The rule is matched against the run's ref, not against your branch name in the abstract: "The deployment branch or tag rule is matched against the GITHUB_REF of the workflow run." The options GitHub documents are "No restriction", "Protected branches only", and "Selected branches and tags", and the third is where tags trip people up. A release-triggered deploy has a tag ref, so a policy that lists main rejects it. GitHub also warns that "Deployment workflow runs triggered by tags with the same name as a protected branch and forks with branches that match the protected branch name cannot deploy to the environment."
Common causes
The github-pages environment does not allow the ref
The loudest of the three and the one with no step log. It fires on a tag, on a release-triggered run, and on any ref that does not match the environment's deployment branch and tag rule. Because the failure is an annotation on the job rather than a step, the run looks like the action never started, which it did not.
The Pages source is still a branch
A repository that published from gh-pages before it moved to a workflow keeps that setting until someone changes it. The API refuses the create call with a 400 and says which branch it will accept, and the action passes that sentence through. In our experience it is the most common 400 on a repository that used to deploy a Jekyll site.
The deployment reached a terminal status after it was created
The create call succeeded, so permissions and the ref were both fine, and the deployment failed on the Pages side. The sentence in the log names which terminal status it was, and only deployment_content_failed points at your artifact rather than at the service.
The artifact carries content Pages will not publish
Hard links, symlinks and total size are the three the action names. A build that copies a tree with cp -al, or one that ships a full node_modules, produces exactly this. It is the only cause here whose fix is in the build job.
How to fix it
Work out which gate stopped the deploy before changing anything
- Open the deploy job. If it has no steps, the environment refused the ref and the answer is in the environment settings.
- If the deploy step ran and the message starts
Failed to create deployment, the API refused the call and the status code in that line is the diagnosis. - If the step created a deployment and then printed one of the fixed sentences above, the deployment failed after creation and the sentence names which way.
Allow the ref you are actually deploying from
In the repository settings, open the github-pages environment and look at deployment branches and tags. Either choose no restriction, or add a pattern that matches the run's ref. Remember the matching is against GITHUB_REF, so a workflow triggered by a release needs a tag pattern and not a branch name. GitHub notes that wildcards will not match a slash, so a pattern of release/* does not cover release/2026/09.
Switch the Pages source to GitHub Actions
This is the repair for the 400 that names a branch. In the repository's Pages settings, set the build and deployment source to GitHub Actions. Until that is done, the API will keep telling you which branch it expects, no matter what the workflow file says.
deploy:
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- id: deployment
uses: actions/deploy-pages@v5Fix the artifact when the status is deployment_content_failed
- Check the upload step for symlinks and hard links;
find <dir> -type landfind <dir> -links +1 -type fboth print quickly. - Check the size of the directory you are uploading, and compare it against the warning line the action prints before it creates the deployment.
- Rebuild, re-upload, and re-run the deploy job on its own rather than the whole workflow.
Gate two: the Pages API refuses the create call
If the job did start and the deploy step ran, the next gate is POST /repos/{owner}/{repo}/pages/deployments. The action logs Creating Pages deployment failed from src/internal/api-client.js and then builds a message from the status code. For a 400 it appends the API's own words, which is how this shape reads in a real run:
Error: Failed to create deployment (status: 400) with build version efba898182be4068f5a83d356f1a5bf241229025. Responded with: Invalid deployment branch and no branch protection rules set in the environment. Deployments are only allowed from gh-pagesGate three: the deployment is created and then reports failure
Once the deployment exists, the action polls GET /repos/{owner}/{repo}/pages/deployments/{deploymentId} and reads a status string. src/internal/deployment.js holds two maps of those strings to sentences, one it treats as terminal and one it treats as temporary, and the sentence in your log tells you which map it came from. This is the table to read when the deploy step ran for a while and then gave up.
| Status from the API | Sentence the action prints | What the action does next |
|---|---|---|
deployment_failed | Deployment failed, try again later. | Fails the step |
deployment_content_failed | Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB. | Fails the step |
deployment_cancelled | Deployment cancelled. | Fails the step |
deployment_lost | Deployment failed to report final status. | Fails the step |
deployment_attempt_error | Deployment temporarily failed, a retry will be automatically scheduled... | Warns and keeps polling |
not_found | Deployment not found. | Warns and keeps polling |
unknown_status | Unable to get deployment status. | Warns and keeps polling |
The content gate is about the artifact, not the ref
The deployment_content_failed sentence is the odd one in that table, because it names something you fix in the build rather than in a setting. The action warns before it gets there: its create method checks the artifact size against one gigabyte and logs that the uploaded size "exceeds the allowed size of 1 GB. Deployment might fail." The gigabyte in that warning and the ten in the terminal sentence are two thresholds rather than a contradiction, and actions/upload-pages-artifact states both: "The GitHub Pages officially supported maximum size limit is 1GB, so the subsequent deployment of larger tarballs are not guaranteed to succeed", and "there is also an unofficial absolute maximum size limit of 10GB, which Pages will not even attempt to deploy". GitHub's published Pages limits carry the same supported figure and the mechanism behind it: a site "may be no larger than 1 GB", and deployments "will timeout if they take longer than 10 minutes". So one gigabyte is the limit, stated softly because a larger site sometimes still lands inside the timeout, and ten is where Pages stops trying. The repair is in whatever produced the site: a symlink into node_modules, a hard link left by a copy step, or an output larger than you think. Environment settings will not move it, because this gate is downstream of both ref checks.
Why there is no recorded run on this page
Gate one never reaches a runner, so there is no job to record. Gate two and gate three are answers from the Pages service about a specific repository's settings and a specific artifact, and a run of ours on our own repository would only prove that our settings are configured, which is not the reader's question. Both log excerpts here are quoted from issues on the action's own repository, and the mapping table is read from the action's source rather than reproduced.
How to prevent it
- Decide once whether the
github-pagesenvironment restricts refs, and write the pattern for the trigger you actually use. - Move the Pages source to GitHub Actions the day you add the workflow, not the day the deploy first fails.
- Keep the upload step pointed at a build output directory, never at the repository root.
- Pin
actions/deploy-pagesto a major tag and read its release notes, since the status strings and messages live in its source.
Frequently asked questions
Why does my deploy job fail with no steps at all?
What does "Deployments are only allowed from gh-pages" mean?
Failed to create deployment (status: 400) line, with the API response quoted after Responded with:. The fix is in the repository Pages settings, where the build and deployment source becomes GitHub Actions.Is "Deployment failed, try again later." worth retrying?
deployment_attempt_error, not_found and unknown_status, the first of which prints Deployment temporarily failed, a retry will be automatically scheduled... instead. Deployment failed, try again later. comes from the map of statuses the action treats as final, so it has stopped polling deliberately.Can I deploy Pages from a tag or a release?
GITHUB_REF, and a release-triggered run carries a tag ref. Add a tag pattern to the github-pages environment, or choose no restriction, and keep in mind that a wildcard will not match a slash.Related guides
References
- GitHub Actions: deployments and environments, deployment branches and tags
- actions/deploy-pages: src/internal/deployment.js, the status maps and the create catch
- actions/deploy-pages#151: a tag refused by the github-pages environment
- actions/deploy-pages#108: a 400 quoting the Pages source branch rule
- actions/upload-pages-artifact: the artifact contract, 1 GB supported and 10GB absolute
- GitHub Actions documentation