YAML Booleans Are Traps — How Yes, No, True, False, On, Off Behave

Consider this YAML configuration:

country: NO
feature_on: on
debug: false
postal_code: 75001

And consider what Python's YAML parser does with it:

import yaml
print(yaml.safe_load("""
country: NO
feature_on: on
debug: false
postal_code: 75001
"""))
# {'country': False, 'feature_on': True, 'debug': False, 'postal_code': 75001}

The country code "NO" becomes the boolean False. The feature flag "on" becomes True. The postal code 75001 becomes an integer.

No warnings. No errors. Just silently wrong data.

This is the most dangerous YAML "feature" and one that has caused real production bugs — from misconfigured Kubernetes pods to broken CI/CD pipelines to incorrect billing configurations.

This guide covers every YAML boolean trap, how parsers decide what is a boolean, and exactly how to prevent it.


YAML 1.1 Boolean Values (The Default)

YAML 1.1 recognizes the following unquoted values as booleans:

True values:

  • true, True, TRUE
  • yes, Yes, YES
  • on, On, ON

False values:

  • false, False, FALSE
  • no, No, NO
  • off, Off, OFF

That is 18 different string forms that evaluate to two boolean values.

Why This Exists

YAML's designers wanted configuration files to feel natural. Writing enabled: yes reads more like English than enabled: true. Writing ssl: on is shorter than ssl: true.

This is fine when the platform expects booleans. It is catastrophic when the platform expects strings.

The Real-World Damage

Country codes:

address:
  country: NO  # becomes boolean False, not "NO"

Kubernetes annotations with "on" or "off":

metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: "on"  # must be quoted

Feature flags in application config:

features:
  new_checkout: yes  # probably correct if app expects boolean
  old_checkout: no   # probably correct

The problem is ambiguity — you cannot tell by looking at the YAML whether yes is intended as a boolean or a string.


YAML 1.2 Booleans (Slightly Better)

YAML 1.2, released in 2009, deprecated the yes/no/on/off boolean forms. In YAML 1.2, only true and false (case-insensitive) are booleans.

# YAML 1.2 behavior
enabled: yes     # string "yes"
disabled: no     # string "no"
active: on       # string "on"
inactive: off    # string "off"
flag: true       # boolean True
flag: false      # boolean False

This is better, but most parsers still default to YAML 1.1:

ParserDefault Version
PyYAML1.1
ruamel.yaml1.2 (defaults to safe mode)
Go yaml.v21.1
Go yaml.v31.2
Ruby Psych1.1
SnakeYAML (Java)1.1
Kubernetes1.1
Docker Compose1.1

If you are unsure which version your parser uses, assume YAML 1.1 with all 18 boolean forms active.


Boolean Traps by Platform

Kubernetes

Kubernetes enforces strong schemas, so boolean traps are less common but still appear:

spec:
  containers:
    - env:
        - name: FEATURE_FLAG
          value: on  # becomes "True" string via K8s env — usually fine

Kubernetes passes everything in env values as strings, so the boolean conversion does not matter for environment variables. But for structured fields:

spec:
  containers:
    - securityContext:
        readOnlyRootFilesystem: yes  # boolean True

This works because Kubernetes expects a boolean. The trap is only when you expect a string.

Docker Compose

Docker Compose environment variables using mapping syntax:

services:
  app:
    environment:
      NODE_ENV: production
      FEATURE_X: on  # boolean True, then stringified to "True"
      COUNTRY: NO    # boolean False, then stringified to "False"

The container receives FEATURE_X=True and COUNTRY=False — almost certainly not what you intended.

Fix: Use the = syntax for environment variables to avoid YAML parsing entirely:

environment:
  - NODE_ENV=production
  - FEATURE_X=on
  - COUNTRY=NO

GitHub Actions

GitHub Actions workflow environment variables:

env:
  CI: true     # YAML boolean True, GitHub converts to "true"
  NO_COLOR: 1  # integer

GitHub Actions converts everything to strings for environment variables, so the boolean trap is less harmful — true becomes "true". But in with: parameters:

- uses: actions/some-action@v1
  with:
    debug: yes  # YAML boolean True

The action receives a boolean, not a string. If the action expects a string "yes", this breaks.

Helm Values

# values.yaml
global:
  feature: on
  region: no

Helm template rendering may convert these unexpectedly:

{{ .Values.global.feature }} → true
{{ .Values.global.region }} → false

How to Detect Boolean Traps

Python

import yaml

def check_booleans(data, path=""):
    """Recursively check for values that might be boolean traps."""
    if isinstance(data, dict):
        for key, value in data.items():
            check_booleans(value, f"{path}.{key}")
    elif isinstance(data, list):
        for i, item in enumerate(data):
            check_booleans(item, f"{path}[{i}]")
    elif isinstance(data, bool):
        print(f"WARNING: Boolean at {path}: {data}")

config = yaml.safe_load(open("config.yml"))
check_booleans(config)

Go

data := make(map[string]interface{})
err := yaml.Unmarshal([]byte(yamlContent), &data)
// Check types with type assertion
for k, v := range data {
    switch v.(type) {
    case bool:
        fmt.Printf("WARNING: %s is boolean: %v\n", k, v)
    }
}

Manual Inspection

Use repr() in Python or JSON.stringify() in JavaScript to see the actual types after parsing.


The Fix: Quote Everything Ambiguous

The only reliable prevention is quoting:

# Unsafe — YAML decides types
country: NO
active: yes
debug: false

# Safe — all strings
country: "NO"
active: "yes"
debug: "false"

If you are building a system that reads YAML configs, consider using a schema validator (like JSON Schema or Pydantic) that rejects unexpected types:

from pydantic import BaseModel

class Config(BaseModel):
    country: str
    active: str
    debug: str

With schema validation, country: NO becomes country: False (boolean), but Pydantic rejects it because it expects a string. The error message immediately reveals the boolean trap.


Platform-Specific Boolean Behavior

Ruby YAML (Psych)

Ruby's Psych parser (default in Rails) converts yes/no/on/off to booleans in YAML 1.1 mode but can be configured to YAML 1.2.

JavaScript/Node.js

const yaml = require('js-yaml');
const doc = yaml.load("key: yes");
console.log(typeof doc.key); // 'boolean'
console.log(doc.key);        // true

js-yaml defaults to YAML 1.1. Use yaml.load() with schema options to control behavior.

.NET

YamlDotNet defaults to YAML 1.1 boolean behavior. Configure with DeserializerBuilder().WithNodeDeserializer(...) for custom handling.


The YAML 1.1 vs 1.2 Migration Problem

The shift from YAML 1.1 to 1.2 has been slow because:

  1. Ecosystem inertia — most tools default to 1.1
  2. Backward compatibility — switching to 1.2 breaks configs that rely on boolean parsing
  3. Spec stalemate — YAML 1.2 adoption has been slow for two decades

If you control both the parser and the config files, use YAML 1.2 explicitly:

from ruamel.yaml import YAML
yaml = YAML(typ='safe')
yaml.version = (1, 2)

This disables yes/no/on/off boolean parsing. But you cannot control what parser your deployment platform uses.


Boolean Trap Checklist

Before deploying any YAML configuration, check for these patterns:

  • Values that look like country codes (NO, ON, off)
  • Feature flag strings ("on", "off", "yes", "no")
  • Boolean-like environment variable values
  • Version numbers with leading zeros (0123)
  • Number-like strings (1.0, 1e6, 0xFF)
  • Null-like values (null, ~, empty)
  • Values in mappings — they are parsed; values in - key=value lists are not

For most projects, the checklist item that catches the most bugs is: quote any value that could be interpreted as a boolean or number if it is meant to be a string.


FAQ

Does YAML treat yes as a boolean?

Yes, in YAML 1.1 — the version most parsers still default to — the unquoted value yes is parsed as the boolean true. The same applies to Yes, YES, and all case variations. YAML 1.2 deprecated this behavior, but because most parsers (PyYAML, Go yaml.v2, Ruby Psych, SnakeYAML) default to 1.1 mode, you cannot rely on 1.2 behavior. If you need yes as a string, quote it: "yes".

What values are considered booleans in YAML?

In YAML 1.1, the following unquoted values are parsed as booleans: true, True, TRUE, false, False, FALSE, yes, Yes, YES, no, No, NO, on, On, ON, off, Off, OFF. In YAML 1.2, only true and false (case-insensitive) are booleans. The yes/no/on/off forms are treated as strings in YAML 1.2. Always quote these values if you intend them as strings, regardless of which YAML version your parser uses.

How do I prevent YAML from interpreting a value as a boolean?

Quote the value. Single quotes ('yes') and double quotes ("yes") both prevent YAML from performing implicit type conversion. For environment variables in Docker Compose, use the - KEY=value list syntax instead of the mapping syntax to bypass YAML parsing entirely. For application configuration, use a schema validator (like Pydantic or JSON Schema) that rejects type mismatches and reveals when a boolean was silently substituted for a string.

Why does country: NO become False in YAML?

Because NO matches one of YAML 1.1's boolean patterns — it is the uppercase form of the false boolean value no. The YAML parser sees NO, checks it against its boolean pattern list, finds a match, and converts it to false without any warning. This is the most common boolean trap in real-world YAML files because country codes (NO for Norway, ON for Ontario) happen to overlap with YAML's boolean keywords. The fix is always to quote country codes: country: "NO".

Is on a boolean in YAML?

Yes, in YAML 1.1, the unquoted value on is parsed as the boolean true. The lowercase form on and the uppercase form ON are both recognized as true. This creates problems when you store feature flag values as on or off — YAML converts them to booleans, and downstream code expecting strings receives the stringified version of the boolean ("True" or "False") instead of the original "on" or "off". Quote feature flag values to preserve the exact string.


Final Thoughts

YAML booleans are a textbook example of convenience features causing real-world harm.

The intent was good — let developers write natural-looking configuration — but the implementation silently mangles data without any indication that something went wrong. A country code becomes a boolean. A feature flag becomes a different boolean. A version number becomes a float.

The fix is simple: quote values that could be misinterpreted. Country codes, feature flags, version strings, number-like identifiers — they all get quotes.

Schema validation catches the rest. If your code expects a string and receives a boolean, the validator will reject it and tell you exactly which field is wrong.

For a quick sanity check on any YAML file with potential boolean traps, paste it into a YAML formatter and inspect the output types. If NO shows up as False, you have found a boolean trap that would have silently corrupted your configuration.

For more on implicit typing, see Do I Need Quotes for Strings in YAML? and Why Your YAML Is Invalid. To find the best tools for catching these issues early, check Best YAML Formatter Tools.