Fix: YAML 'mapping values are not allowed here' and Other YAML Syntax Errors

The Error

You edit a YAML file and get one of these errors:

yaml: line 5: mapping values are not allowed here
yaml: line 8: could not find expected ':'
yaml: line 12: did not find expected key
Error: yaml: line 3: found character that cannot start any token
yaml: line 7: did not find expected '-' indicator

You might see these from Docker Compose, Kubernetes (kubectl apply), GitHub Actions, Ansible, CI/CD pipelines, or any tool that parses YAML. The wording varies slightly between parsers, but they all mean the same thing: your YAML has a syntax error.

Why This Happens

YAML is whitespace-sensitive. Unlike JSON, where braces and brackets define structure, YAML uses indentation. This makes it readable but extremely easy to break. A single wrong space, tab character, or missing quote can make the entire file invalid.

The most common causes:

  • Tabs instead of spaces. YAML forbids tabs for indentation. Period.
  • Inconsistent indentation levels. Mixing 2-space and 4-space indentation within the same block.
  • Unquoted special characters. Colons, #, @, *, {, }, [, ], and other characters have special meaning in YAML.
  • Colons inside values without quoting. A colon followed by a space (:) is a key-value separator. If your value contains one, you need quotes.
  • Wrong multiline string syntax. Missing or incorrect use of | and > block scalars.
  • Trailing spaces after a key or at the end of a line.
  • BOM (Byte Order Mark) characters. Invisible characters at the start of the file, often added by Windows editors.

Fix 1: Replace Tabs With Spaces

This is the number one cause of YAML errors. YAML does not allow tabs for indentation. Not sometimes. Not with a config flag. Never.

The error often looks like:

found character '\t' that cannot start any token

But tab-related issues can also trigger mapping values are not allowed here if the tab-based alignment lands in the wrong spot.

Check for tabs:

grep -P '\t' your-file.yaml

Or with cat:

cat -A your-file.yaml

Tabs show up as ^I. Spaces show up as regular whitespace.

Replace all tabs with spaces:

sed -i 's/\t/  /g' your-file.yaml

Configure your editor to use spaces for YAML files. Every major editor supports this:

  • VS Code: Add to your settings (settings.json):
    "[yaml]": {
      "editor.insertSpaces": true,
      "editor.tabSize": 2
    }
  • Vim: Add to .vimrc:
    autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab
  • JetBrains IDEs (IntelliJ, PyCharm, etc.): Go to Settings > Editor > Code Style > YAML and set Use tab character to unchecked, Tab size and Indent to 2.

Fix 2: Fix Indentation Alignment

Every item in a YAML block must be at the same indentation level. Child items must be indented exactly one level deeper than their parent. “One level” is typically 2 spaces, but the only hard rule is consistency.

Broken — mixed indentation:

server:
  port: 8080
    host: localhost    # ❌ 4 spaces, but "port" uses 2

This triggers mapping values are not allowed here because YAML sees host as a continuation of the port value, not as a sibling key.

Fixed:

server:
  port: 8080
  host: localhost      # ✅ same level as "port"

Broken — list items at wrong level:

services:
  web:
    ports:
    - "8080:80"
      - "443:443"     # ❌ extra indentation

Fixed:

services:
  web:
    ports:
      - "8080:80"
      - "443:443"     # ✅ same level

Rule of thumb: If you get mapping values are not allowed here, look at the line the error points to and the line above it. One of them is at the wrong indentation level.

Fix 3: Quote Values That Contain Colons

A colon followed by a space (:) is how YAML separates keys from values. If your value contains this pattern, YAML thinks it’s a nested key.

Broken:

message: Error: something went wrong

YAML parses this as key message with value Error, then tries to parse something went wrong as another mapping. That gives you mapping values are not allowed here.

Fixed:

message: "Error: something went wrong"

This applies to any value containing ::

# ❌ Broken
url: http://localhost:8080
time: 10:30:00
description: Note: this is important

# ✅ Fixed
url: "http://localhost:8080"
time: "10:30:00"
description: "Note: this is important"

Note: A colon at the end of a value without a trailing space is technically valid YAML (key: value:), but quoting it anyway is safer and clearer.

Fix 4: Quote Special Characters

Several characters have special meaning in YAML. When they appear in values, they need quoting.

# ❌ Broken -- these all cause parse errors
password: p@ss#word!
regex: [a-z]+
command: echo {hello}
reference: *default
value: %SECRET%
flag: yes               # Parsed as boolean true, not the string "yes"
version: 3.10           # Parsed as float 3.1, not string "3.10"

# ✅ Fixed -- quote them
password: "p@ss#word!"
regex: "[a-z]+"
command: "echo {hello}"
reference: "*default"
value: "%SECRET%"
flag: "yes"
version: "3.10"

Characters that require quoting when they appear at the start of a value: *, &, !, %, @, `, {, }, [, ], |, >, ', ", #, ,, ?.

Characters that require quoting anywhere in a value: : (colon-space), # (space-hash).

Boolean gotcha: YAML 1.1 (used by many parsers) treats yes, no, on, off, true, false (and various capitalizations) as booleans. This is a notorious source of bugs:

# These are all booleans, not strings
country: NO       # false, not Norway
answer: yes       # true, not the string "yes"
enabled: on       # true

# Quote them to keep them as strings
country: "NO"
answer: "yes"
enabled: "on"

Fix 5: Fix Multiline Strings

YAML has two block scalar styles for multiline strings. Getting the syntax wrong triggers parse errors.

Literal block (|) — preserves newlines exactly:

script: |
  echo "line 1"
  echo "line 2"
  echo "line 3"

Folded block (>) — joins lines with spaces (like a paragraph):

description: >
  This is a long description
  that gets folded into a single
  line with spaces.

Common mistakes:

# ❌ Content must be indented under the block indicator
script: |
echo "not indented"

# ✅ Fixed
script: |
  echo "indented"

# ❌ No content on the same line as the indicator
script: | echo "hello"

# ✅ Fixed
script: |
  echo "hello"

# ❌ Inconsistent indentation within the block
script: |
  echo "line 1"
    echo "line 2"   # extra indent becomes part of the string, which might not be what you want

# ✅ Fixed
script: |
  echo "line 1"
  echo "line 2"

Block chomping indicators control trailing newlines:

# |  -- keeps final newline (default)
# |- -- strips final newline
# |+ -- keeps all trailing newlines

command: |-
  echo "no trailing newline"

This matters in GitHub Actions and shell scripts where a trailing newline can change behavior.

Fix 6: Docker Compose YAML Errors

Docker Compose files are one of the most common places for YAML errors. Here are the most frequent mistakes:

Broken — wrong indentation under services:

services:
web:                      # ❌ should be indented
  image: nginx

Fixed:

services:
  web:                    # ✅ indented under services
    image: nginx

Brokenenvironment variables with unquoted colons:

services:
  app:
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb  # ❌ colons in value

**Fixed — use the mapping syntax or quote:

services:
  app:
    environment:
      DATABASE_URL: "postgres://user:pass@db:5432/mydb"

If Docker Compose can’t connect to your database even with correct YAML, see Fix: PostgreSQL connection refused or Fix: MySQL access denied.

Broken — ports as numbers without quotes:

services:
  web:
    ports:
      - 80:80             # Works, but can cause issues in some parsers
      - 8080:8080

Docker Compose handles unquoted ports, but it’s best practice to quote them:

services:
  web:
    ports:
      - "80:80"
      - "8080:8080"

Validate your Compose file:

docker compose config

This parses the file and outputs the resolved configuration. If there’s a YAML error, it tells you the exact line.

Related: Fix: Docker COPY Failed: File Not Found in Build Context

Fix 7: Kubernetes Manifest YAML Errors

Kubernetes manifests are deeply nested YAML, making indentation mistakes easy.

Broken — wrong indentation in Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
containers:               # ❌ should be indented under spec
  - name: app
    image: nginx

Fixed:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:             # ✅ indented under spec
    - name: app
      image: nginx

Broken — list items not aligned:

spec:
  containers:
    - name: app
      image: nginx
      ports:
        - containerPort: 80
      env:
      - name: DB_HOST          # ❌ not aligned with the key above
        value: localhost

Fixed:

spec:
  containers:
    - name: app
      image: nginx
      ports:
        - containerPort: 80
      env:
        - name: DB_HOST        # ✅ indented under env
          value: localhost

Validate before applying:

kubectl apply --dry-run=client -f manifest.yaml

Or use a linter:

kubeval manifest.yaml

Related: Fix: The Connection to the Server localhost:8080 Was Refused (kubectl)

Fix 8: GitHub Actions Workflow YAML Errors

GitHub Actions workflows have a specific YAML structure. A syntax error causes the workflow to fail with:

.github/workflows/ci.yml: Invalid workflow file

Broken — wrong indentation under steps:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: npm install
    name: Install             # ❌ name should be part of the step

Fixed:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install
        run: npm install

Broken — multiline run without block scalar:

steps:
  - name: Build
    run: npm install
         npm run build        # ❌ YAML sees this as a continuation, not a second command

Fixed:

steps:
  - name: Build
    run: |
      npm install
      npm run build

Broken — expression with unquoted braces:

steps:
  - name: Deploy
    if: github.ref == 'refs/heads/main'
    env:
      TOKEN: ${{ secrets.DEPLOY_TOKEN }}   # ✅ this is fine -- GitHub Actions handles it
      MESSAGE: {deploy started}            # ❌ bare braces are a YAML flow mapping

Fixed:

      MESSAGE: "{deploy started}"

Validate GitHub Actions workflows locally with actionlint:

actionlint .github/workflows/ci.yml

Fix 9: Use a YAML Linter

Don’t eyeball YAML errors. Use a linter.

yamllint (the gold standard for YAML linting):

pip install yamllint
yamllint your-file.yaml

Example output:

your-file.yaml
  3:1       error    wrong indentation: expected 2 but found 4  (indentation)
  5:12      error    too many spaces after colon  (colons)
  8:1       error    trailing spaces  (trailing-spaces)

Configure yamllint with a .yamllint.yml file:

extends: default
rules:
  line-length:
    max: 200
  indentation:
    spaces: 2
  truthy:
    check-keys: false

VS Code extensions:

  • YAML (by Red Hat) — validates YAML with schema support for Kubernetes, Docker Compose, GitHub Actions, and more. Install the redhat.vscode-yaml extension.
  • yamllint — integrates yamllint directly into VS Code.

Online validators: If you can’t install tools, paste your YAML into yamllint.com for a quick check. Don’t paste secrets or credentials into online validators.

Fix 10: YAML Anchors and Aliases

YAML anchors (&) and aliases (*) let you reuse blocks of configuration. Incorrect syntax here triggers did not find expected key or could not find expected ':'.

Broken — alias used before anchor is defined:

services:
  web:
    <<: *common            # ❌ alias used before it's defined
  defaults: &common
    restart: always

Fixed — define the anchor first:

x-common: &common
  restart: always

services:
  web:
    <<: *common            # ✅ anchor is defined above

Broken — wrong merge syntax:

defaults: &defaults
  adapter: postgres
  host: localhost

production:
  <<: *defaults
  host: prod-db            # ✅ this correctly overrides host from defaults
  <<: *other               # ❌ duplicate merge key

You cannot use << twice at the same level. Merge multiple anchors with a list:

production:
  <<: [*defaults, *other]
  host: prod-db

Note: Not all YAML parsers support anchors and aliases. GitHub Actions, for instance, supports them in some contexts but not others.

Still Not Working?

Remove BOM characters

If your file was created or edited on Windows, it might have a BOM (Byte Order Mark) at the very start. This invisible character causes errors like:

found character that cannot start any token

Check for BOM:

hexdump -C your-file.yaml | head -1

If the first bytes are ef bb bf, you have a UTF-8 BOM. Remove it:

sed -i '1s/^\xEF\xBB\xBF//' your-file.yaml

Or in VS Code: open the file, click the encoding indicator in the bottom-right bar (it might say “UTF-8 with BOM”), and select Save with Encoding > UTF-8 (without BOM).

Remove trailing whitespace

Trailing spaces are invisible but can cause issues, especially after a colon at the end of a line that should introduce a nested block:

server:·····
  port: 8080

Those trailing spaces after server: can confuse some parsers. Remove all trailing whitespace:

sed -i 's/[[:space:]]*$//' your-file.yaml

Check .yml vs .yaml

Both .yml and .yaml are valid extensions. The YAML specification prefers .yaml, but .yml is widely used. The issue arises when a tool expects a specific filename.

  • Docker Compose: looks for docker-compose.yml or compose.yml (since Compose V2) by default. Not docker-compose.yaml.
  • GitHub Actions: requires files in .github/workflows/ with .yml or .yaml extensions. Both work.
  • Kubernetes: accepts both extensions. No preference.

If your tool isn’t picking up your file, check the expected filename and extension in its documentation.

Multi-document YAML issues

YAML supports multiple documents in a single file, separated by ---. A misplaced separator causes parse errors:

apiVersion: v1
kind: Service
metadata:
  name: my-service
---                        # ✅ document separator
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app

Make sure --- is on its own line with no leading spaces and no trailing content.

Invisible characters from copy-pasting

Copying YAML from websites, PDFs, or Slack often introduces non-breaking spaces (U+00A0), smart quotes, or other invisible characters that look like normal whitespace but aren’t.

Check for non-ASCII characters:

grep -P '[^\x00-\x7F]' your-file.yaml

If you find them, retype the affected lines manually rather than trying to fix individual characters. This is especially common when copying from documentation sites that use typographic quotes (" ") instead of straight quotes (").

Validate against a schema

For tool-specific YAML, validate against the correct schema:

# Docker Compose
docker compose config

# Kubernetes
kubectl apply --dry-run=client -f manifest.yaml

# GitHub Actions
actionlint .github/workflows/*.yml

# Generic YAML
yamllint -d relaxed your-file.yaml

Schema validation catches issues beyond syntax — like misspelled keys, wrong value types, or missing required fields — that a generic YAML parser won’t flag.

Related: Fix: IndentationError: unexpected indent (Python) — similar whitespace-sensitivity issues in Python.

Related Articles