YAML Implicit Typing Pitfalls: Booleans, Nulls, and Leading Zeros
You convert YAML to JSON and suddenly "yes" becomes true, "07" becomes 7, and "null" becomes null. YAML's implicit type conversion is powerful but can cause unexpected bugs when parsers interpret strings as booleans, numbers, or nulls. This guide explains YAML's type inference rules and how to prevent unwanted conversions.
The Problem: YAML Guesses Types
Unlike JSON, which requires explicit types (strings must be quoted, booleans are true/false), YAML infers types from context. This is convenient but error-prone.
Example: Unexpected Type Conversion
# YAML config file
settings:
enabled: yes # Becomes boolean true
code: 007 # Becomes number 7
value: null # Becomes null
country: NO # Becomes boolean false!When converted to JSON:
{
"settings": {
"enabled": true, // Was: "yes"
"code": 7, // Was: "007"
"value": null, // Was: "null"
"country": false // Was: "NO"
}
}The country code NO (Norway) became false because YAML 1.1 treats NO as a boolean. This is the most notorious YAML pitfall.
Boolean Values: More Than True/False
YAML 1.1 recognizes 22 different boolean values. YAML 1.2 simplified this, but many parsers still use YAML 1.1 rules.
YAML 1.1 Boolean Values
True: true, True, TRUE, yes, Yes, YES, on, On, ON, y, Y
False: false, False, FALSE, no, No, NO, off, Off, OFF, n, N
YAML 1.2 Boolean Values (Recommended)
True: true, True, TRUE
False: false, False, FALSE
Real-World Example: Country Codes
# User profile YAML
users:
- name: Alice
country: US # String (not a boolean keyword)
- name: Bob
country: NO # YAML 1.1: false (boolean!)
- name: Carol
country: yes # YAML 1.1: true (boolean!)Converted to JSON (YAML 1.1):
{
"users": [
{"name": "Alice", "country": "US"},
{"name": "Bob", "country": false}, // Bug!
{"name": "Carol", "country": true} // Bug!
]
}Solution: Quote String Values
users:
- name: Bob
country: "NO" # Explicit string, not boolean
- name: Carol
country: "yes" # Explicit string, not booleanNow converts correctly:
{
"users": [
{"name": "Bob", "country": "NO"},
{"name": "Carol", "country": "yes"}
]
}Null Values: Many Ways to Express Nothing
YAML recognizes multiple null representations:
null,Null,NULL~(tilde)- Empty value (key with no value)
Example: Null Representations
# All of these become null in JSON
data:
field1: null
field2: NULL
field3: ~
field4: # Empty value
field5: # Trailing colon, no valueConverted to JSON:
{
"data": {
"field1": null,
"field2": null,
"field3": null,
"field4": null,
"field5": null
}
}Pitfall: Unquoted "null" String
# Configuration with nullable field
database:
password: null # Null value (no password)
api_key: null # Null value (no API key)
# But what if you literally want the string "null"?
log_level: "null" # String "null", not null valueConverted to JSON:
{
"database": {
"password": null // Actual null
},
"api_key": null, // Actual null
"log_level": "null" // String "null"
}Leading Zeros: Octal Number Trap
Numbers with leading zeros can be interpreted as octal (base-8) in YAML 1.1.
Example: Octal Conversion
# Configuration with codes
items:
code1: 007 # Octal 007 = decimal 7
code2: 010 # Octal 010 = decimal 8
code3: 077 # Octal 077 = decimal 63
code4: 100 # Decimal 100 (no leading zero)Converted to JSON (YAML 1.1):
{
"items": {
"code1": 7, // Lost leading zeros
"code2": 8, // Converted from octal
"code3": 63, // Converted from octal
"code4": 100
}
}Real-World Problem: Phone Numbers and Zip Codes
# Address book
contacts:
- name: Alice
zip: 02134 # Boston zip code
- name: Bob
zip: 10001 # NYC zip code
- name: Carol
phone: 07700900123 # UK phone numberConverted to JSON (YAML 1.1):
{
"contacts": [
{"name": "Alice", "zip": 1116}, // Bug! Octal 02134 = decimal 1116
{"name": "Bob", "zip": 4097}, // Bug! Octal 10001 = decimal 4097
{"name": "Carol", "phone": 534643795} // Bug! Lost leading zero
]
}Solution: Quote Numeric Strings
contacts:
- name: Alice
zip: "02134" # Explicit string
- name: Bob
zip: "10001" # Explicit string
- name: Carol
phone: "07700900123" # Explicit stringNow converts correctly:
{
"contacts": [
{"name": "Alice", "zip": "02134"},
{"name": "Bob", "zip": "10001"},
{"name": "Carol", "phone": "07700900123"}
]
}Hexadecimal Numbers
YAML 1.1 recognizes hexadecimal numbers with 0x prefix:
colors:
red: 0xFF0000 # Hex number = 16711680
green: 0x00FF00 # Hex number = 65280
blue: 0x0000FF # Hex number = 255Converted to JSON:
{
"colors": {
"red": 16711680,
"green": 65280,
"blue": 255
}
}If you want to preserve hex strings (e.g., for CSS colors), quote them:
colors:
red: "#FF0000" # String
green: "#00FF00" # String
blue: "#0000FF" # StringScientific Notation
YAML supports scientific notation for floating-point numbers:
measurements:
distance: 1.23e5 # 123000
tiny: 1.23e-5 # 0.0000123
avogadro: 6.022e23 # 6.022 × 10^23Converted to JSON:
{
"measurements": {
"distance": 123000,
"tiny": 0.0000123,
"avogadro": 6.022e+23
}
}Infinity and NaN
YAML 1.1 supports special floating-point values:
special_numbers:
positive_inf: .inf # Positive infinity
negative_inf: -.inf # Negative infinity
not_a_number: .nan # NaN (Not a Number)These convert to special JSON values (implementation-dependent). Most JSON libraries don't support these natively and may serialize them as strings or null.
When to Quote Values
Always quote:
- Country codes (NO, YES, ON, OFF, N, Y)
- Version numbers with leading zeros (01.02.03)
- Phone numbers with leading zeros
- Zip/postal codes with leading zeros
- Hex color codes (#FF0000)
- The literal strings "true", "false", "null", "yes", "no", "on", "off"
- Numbers that should stay as strings
Don't need to quote:
- Simple strings without special characters
- Numbers you want as numbers
- Actual boolean or null values
Real-World Example: Docker Compose
Docker Compose files are YAML and often hit these pitfalls:
# docker-compose.yml
version: "3.8" # Quote! Otherwise becomes float 3.8
services:
app:
image: node:18
environment:
- NODE_ENV=production
- PORT=3000
- ENABLE_FEATURE=yes # Becomes boolean true
- DEBUG=no # Becomes boolean false
ports:
- "3000:3000" # Quote! Port mapping stringThe environment variables ENABLE_FEATURE and DEBUG become booleans, which may not work if your application expects string values.
Fixed Version
version: "3.8"
services:
app:
image: node:18
environment:
- NODE_ENV=production
- PORT=3000
- ENABLE_FEATURE="yes" # Explicit string
- DEBUG="no" # Explicit string
ports:
- "3000:3000"YAML 1.1 vs YAML 1.2
YAML 1.1 (most parsers):
- 22 boolean values (yes/no/on/off/y/n)
- Octal numbers (leading zero)
- Sexagesimal numbers (60:30 = 3630 seconds)
- More lenient, more surprises
YAML 1.2 (recommended):
- Only true/false (case-insensitive)
- No octal numbers
- JSON superset (valid JSON is valid YAML)
- More predictable, fewer surprises
Check your parser's YAML version. Many tools still use YAML 1.1, so it's safer to quote ambiguous values.
Testing YAML Conversions
Use JSON ↔ YAML Converter to test how your YAML converts to JSON. This helps identify unwanted type conversions before they cause bugs.
Test Workflow
- Paste your YAML into YAML Converter
- Convert to JSON
- Check if types match expectations
- Add quotes where needed
- Re-convert and verify
Quick Reference: Type Inference Rules
| YAML Value | Interpreted As | JSON Result | How to Force String |
|---|---|---|---|
yes | Boolean | true | "yes" |
no | Boolean | false | "no" |
on | Boolean | true | "on" |
off | Boolean | false | "off" |
NO | Boolean (YAML 1.1) | false | "NO" |
null | Null | null | "null" |
~ | Null | null | "~" |
007 | Octal number (YAML 1.1) | 7 | "007" |
0x10 | Hex number | 16 | "0x10" |
1.23e5 | Scientific notation | 123000 | "1.23e5" |
.inf | Infinity | Infinity (special) | ".inf" |
Best Practices
- Quote ambiguous values - When in doubt, add quotes
- Test conversions - Use YAML↔JSON converter to verify types
- Prefer YAML 1.2 - Use parsers that support YAML 1.2 when possible
- Document your schema - Specify expected types in comments or docs
- Use linters - YAML linters can warn about implicit conversions
- Be explicit - Write
true/falseinstead ofyes/no
Summary
YAML's implicit type conversion can cause unexpected bugs:
yes/no/on/offbecome booleansnull/~/empty values become null- Leading zeros trigger octal conversion
- Country codes like NO become false
0xprefix creates hex numbers
Solution: Quote values when you need explicit strings. Test your YAML conversions with JSON ↔ YAML Converter to catch type issues early.
When working with configuration files, prefer explicit quoting over relying on type inference. It makes your config files more predictable and prevents hard-to-debug type coercion bugs.
Need to convert between JSON and YAML safely? Use JSON ↔ YAML Converter to test conversions and verify types. Also check out JSON Formatter for validating JSON structure after conversion.