GitHub Actions YAML anchors, aliases, and the merge key that is not one
GitHub Actions YAML anchors are documented as usable on GitHub.com, and have been since September 2025, which reverses years of advice saying they are not. What has not changed is the merge key: the documentation shows only anchors and aliases, and the workflow schema has no property to receive a key written as two angle brackets.

What this error means
One of three things. An anchor and alias pair works fine and you were told it could not, which is the happy case and usually means you are reading an older answer. Or the workflow is rejected with a sentence about anchors not being supported, which points at the product or the parser doing the reading rather than at your file. Or the anchors resolve, the merge key does not, and you get an annotation naming a key you did not think you had written.
Anchors are not currently supported. Remove the anchor 'common'What the documentation now says, and for which products
The reference page on reusing workflow configurations carries a section headed YAML anchors and aliases. It explains that an anchor marked with an ampersand identifies content you want to reuse and an alias marked with an asterisk repeats it elsewhere, links the YAML specification, and gives two worked examples: one sharing an env map between two jobs, and one reusing an entire job configuration.
That section is recent, and it is datable. The commit history of that file in github/docs reaches back to 17 September 2025, and the commit that starts it adds the heading, the opening sentence, both worked examples and the version condition in one change. So the documentation has said this since September 2025, and an answer written before then is not wrong about its own moment. What that date fixes is the documentation rather than the runtime: it says when GitHub started publishing the claim, not when any parser changed, and the claim on this page is the documentary one.
The gating on that section is worth reading as carefully as the text. The page as a whole is published for GitHub Free, Pro and Team, for Enterprise Cloud and for Enterprise Server, but the anchors section is wrapped in a condition that selects only the first two. The intro sentence of the page carries the same condition, mentioning anchors and aliases only on those products. So the documentation does not claim the feature for Enterprise Server, and an answer that says anchors work should say where.
On the parser side, the workflow parser published in actions/runner has a parse option named AllowAnchors. When it is off, the reader raises the message quoted above for an anchor on a scalar, on a mapping start or on a sequence start. When it is on, the reader records anchors by position and replays them when it meets an alias, and raises a different message for an alias whose anchor it has never seen. GitHub's pre-run validation service is not open, so this page does not claim which setting that service uses; it reports what the published parser offers and what the documentation states.
| Construct | In the documentation | In the published schema |
|---|---|---|
&name anchor | a section, gated to GitHub.com and Enterprise Cloud | handled by the reader, behind a parse option |
*name alias | the same section, with two examples | replayed from the recorded anchor |
<< merge key | not mentioned anywhere | no property, and no loose key on a job |
| An alias with no anchor | not mentioned | the reader raises an unknown anchor error |
Common causes
The advice you found predates the documented support
For years the correct answer was that workflows did not take anchors, and a great deal of writing says so. The reference page now carries a section describing them, gated to GitHub.com and Enterprise Cloud, so the old answer is no longer the whole answer.
A merge key was used to extend a map rather than replace it
The merge key is a separate convention from anchors and aliases, it is absent from the documentation, and the schema has no property for it. Where it lands on a fixed mapping it is rejected by name, which reads as a strange error about a key you did not write.
The file runs somewhere the section does not apply
The documentation section is conditioned on GitHub Free, Pro, Team and Enterprise Cloud. A workflow shared with an Enterprise Server installation is outside what the documentation claims, so reuse that works in one place should not be assumed to work in the other.
An alias refers to an anchor that is not in the same file
Aliases are resolved within one document. In our experience this arrives when a workflow is split, because the anchor stays in the file that was not moved and the alias goes with the jobs that were.
How to fix it
Use an alias to supply a whole value
Anchor a complete map or a complete job and alias it where you want the same thing again, which is exactly what the documented examples do. Replacing a value is supported; extending one is what the merge key was for and there is no substitute for it here.
jobs:
job1:
env: &env_vars
NODE_ENV: production
steps:
- run: npm run build
job2:
env: *env_vars
steps:
- run: npm testMove shared steps into a composite action
When the repetition is a sequence of steps, put them in a composite action and call it. The interface is declared, the reuse crosses files and repositories, and the action appears as its own unit in the run, none of which an alias gives you.
- uses: ./.github/actions/setup
with:
node-version: '22'Move a shared job into a reusable workflow
- Add
on: workflow_callto the file holding the job, with any inputs it needs declared and typed. - Call it from the other workflows with
usesat job level. - Pass values through
withrather than through an alias, so the contract is visible on both sides. - Keep anchors for small repetitions inside one file, where they cost nothing to read.
Check where the file will run before relying on anchors
If the same workflow is used on Enterprise Server as well as GitHub.com, treat anchors as unavailable, because the documentation section that describes them is not published for that product. Reusable workflows and composite actions carry no such condition.
Where a merge key is refused and where it is quietly accepted
Because the merge key arrives as an ordinary key, what happens to it depends on the mapping it lands in. The workflow root is a mapping with a fixed set of properties and no loose keys, and a job is a choice between two mappings that also have fixed properties and no loose keys. A key of two angle brackets matches no property in either place, so the reader raises its Unexpected value for it, naming the key.
Some mappings are not fixed. A workflow level env map takes any non-empty string as a key, which means a merge key written there is accepted as the name of an environment variable. Nothing fails, and a variable with a peculiar name is created. That is the version of this that is hardest to notice, because the only symptom is that the values you expected to merge are absent.
The practical rule is to use anchors and aliases to replace a whole value and never to extend one. Aliasing a complete env map onto a second job is what the documented examples do. Splicing extra keys into an existing map is the thing the merge key was for, and there is nothing in the workflow language that does it.
jobs:
test: &base_job
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: npm test
alt-test: *base_jobWhat anchors do not give you
An alias is textual reuse inside one file, resolved before anything about workflows is considered. It cannot cross file boundaries, so it is no help at all for sharing configuration between repositories or even between two workflow files in the same repository. It does not deduplicate a matrix. And because the expansion happens before the schema is applied, an alias that produces an invalid shape produces an ordinary schema error at the aliased position, which can be a long way from the anchor that caused it.
For reuse across files, the mechanisms are reusable workflows called with uses at job level, and composite actions called with uses at step level. Both are checked, both have declared interfaces, and both appear in the run as distinct units, which anchors do not.
The honest summary is that anchors have become available for the small case they are good at, removing a little repetition within one file, and they have not replaced anything. If you were planning to restructure a repository around them, plan around reusable workflows instead.
Why there is no recorded run on this page
The subject of this page is a difference between products and between eras, and a runner cannot record either. A green run on GitHub.com proves nothing about the same file on Enterprise Server, which is precisely the boundary the documentation draws with its version condition, and no Latchkey runner has an Enterprise Server to compare against.
The claims that matter are therefore documentary and source level: a documentation section with a stated version gate, a parse option with a default, and two reader paths with distinct messages. Recording a workflow that happens to work today would invite exactly the mistake this page exists to correct, which is treating one observation of one product as a rule.
How to prevent it
- Keep anchors to small repetitions inside one file, and reuse across files with
uses. - Never use a merge key in a workflow, in any position.
- Note in a comment when a file depends on anchors, so a move to another product is a decision.
- Prefer a declared interface over textual reuse whenever anyone else will edit the file.
Frequently asked questions
Do YAML anchors work in GitHub Actions workflows?
Can I use the YAML merge key in a workflow?
What does "Anchors are not currently supported" mean?
Should I use anchors instead of reusable workflows?
Related guides
References
- GitHub Actions: reusing workflow configurations, YAML anchors and aliases
- actions/runner: YamlObjectReader.cs, the anchor paths
- actions/runner: ParseOptions.cs, the AllowAnchors option
- YAML 1.2.2 specification: node anchors and aliases
- github/docs: the commit that added the anchors section, 17 September 2025
- GitHub Actions documentation