JSON Keys Must Be Double-Quoted: Why the Spec Requires It and How to Fix SyntaxErrors
Quick answer
💡JSON.parse throws SyntaxError when object keys lack double quotes. The JSON specification (RFC 8259) defines object names as strings, and strings must use double-quote delimiters. JavaScript object literals allow unquoted keys, but JSON is not JavaScript. Use JSON.stringify on the source object to produce valid JSON, or run the text through ToolDock JSON Validator to see the exact error position.
Error symptoms
- ✕
SyntaxError: Expected double-quoted property name in JSON at position 1 - ✕
SyntaxError: Unexpected token n in JSON at position 1 (Node.js v16 and earlier) - ✕
json.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) in Python - ✕
jq: parse error (Invalid numeric literal at line 1, column 2) when processing {key: 'value'} - ✕
JSON parse failed at offset 1: unquoted key or unexpected identifier - ✕
SyntaxError: JSON.parse: expected property name or '}' at line 1 column 2 of the JSON data (Firefox)
Common causes
- •A JavaScript object literal was copied directly into a JSON field or config file without converting it to JSON format
- •A configuration file written as a JS module — using module.exports = {key: value} — was read and passed directly to JSON.parse
- •A developer used single quotes around keys, which is valid JavaScript but not valid JSON
- •An API response was manually edited or constructed by hand and the editor used unquoted keys because they look cleaner
- •A template string or string concatenation produced JSON-like text with unquoted keys instead of using JSON.stringify
- •A YAML or TOML configuration was misidentified as JSON and passed to a JSON parser
When it happens
- •Pasting JavaScript object syntax from browser console output into a JSON configuration file or API test body
- •Reading a .js config file (like webpack.config.js or babel.config.js) with fs.readFileSync and then calling JSON.parse on the text
- •Constructing JSON strings manually with template literals instead of building an object and calling JSON.stringify
- •Copying example code from documentation that uses JS object notation without converting to valid JSON format
- •Passing YAML or relaxed JSON (JSON5) to a standard JSON parser that does not support those extensions
Examples and fixes
A developer builds a configuration object in JavaScript syntax and then accidentally passes the source text — not the stringified version — to JSON.parse, or copies the console output of the object as a JSON string.
JavaScript object literal passed to JSON.parse
❌ Wrong
// This is valid JavaScript object syntax
const configText = `{
host: 'db.internal',
port: 5432,
database: 'appdb',
ssl: true
}`;
// Passing JS object syntax to JSON.parse
const configRecord = JSON.parse(configText);
// SyntaxError: Expected double-quoted property name in JSON at position 4
// (position 4 is the 'h' in 'host' — an unquoted key)✅ Fixed
// Build the config as a JavaScript object first
const configObject = {
host: 'db.internal',
port: 5432,
database: 'appdb',
ssl: true
};
// Serialize with JSON.stringify to produce valid JSON text
const configJson = JSON.stringify(configObject);
// '{"host":"db.internal","port":5432,"database":"appdb","ssl":true}'
// Now JSON.parse works because all keys are double-quoted
const configRecord = JSON.parse(configJson);
console.log(configRecord.host); // 'db.internal'JSON.stringify converts a JavaScript object into valid JSON text by double-quoting all keys and converting all string values to double-quoted strings. The original configText is a template literal containing JavaScript object syntax, which is not JSON. The correct workflow is to build the data as a JavaScript object using normal object literal syntax, then call JSON.stringify to produce valid JSON text, and then call JSON.parse only when you have received valid JSON from an external source. Using JSON.stringify followed immediately by JSON.parse on the same object is a common way to deep-clone an object.
A Node.js script reads a JavaScript config file using the file system API and then tries to parse it as JSON. The file uses valid JavaScript syntax with unquoted keys, which is not valid JSON.
Config file read as text and passed to JSON.parse
❌ Wrong
const fs = require('fs');
// serviceConfig.js contains:
// module.exports = { retryLimit: 3, timeoutMs: 5000, endpoint: '/api/v2' }
const rawText = fs.readFileSync('./serviceConfig.js', 'utf8');
const serviceSettings = JSON.parse(rawText);
// SyntaxError: Unexpected token 'm' in JSON at position 0
// (the 'm' in 'module.exports' is the first character, not '{')✅ Fixed
// Option A: use require() to load a JS config file
const serviceSettings = require('./serviceConfig.js');
console.log(serviceSettings.retryLimit); // 3
// Option B: rename the file to serviceConfig.json and use double-quoted keys
// serviceConfig.json:
// { "retryLimit": 3, "timeoutMs": 5000, "endpoint": "/api/v2" }
const fs = require('fs');
const rawJson = fs.readFileSync('./serviceConfig.json', 'utf8');
const configRecord = JSON.parse(rawJson);
console.log(configRecord.endpoint); // '/api/v2'A .js file containing module.exports = {...} is a JavaScript module, not a JSON file. JSON.parse cannot execute JavaScript — it only understands the JSON subset of JavaScript syntax. For JavaScript config files, use require() which executes the module and returns the exported value. If you want a config file that JSON.parse can read, create a .json file with all keys double-quoted and all string values double-quoted. JSON files cannot contain module.exports or any JavaScript expressions — only static data.
Why the JSON spec mandates quoted property names
RFC 8259, the JSON specification, defines an object as an unordered collection of zero or more name-value pairs where each name is a string. The specification then defines a string as a sequence of Unicode characters enclosed in quotation marks, and specifies the quotation mark as the character U+0022, which is the double-quote character. This means the JSON specification does not merely prefer quoted keys — it requires them as part of the formal grammar. An object key without double quotes is not JSON.
The reason this requirement exists is unambiguous parsing. Douglas Crockford, who formalized JSON from JavaScript's object literal syntax, made the deliberate decision to require quoted keys to prevent a class of parsing ambiguity. In a JavaScript object literal, {name: value} is valid because the JavaScript parser knows that any bare identifier in the key position is a property name. But a generic parser reading text without a JavaScript grammar has no reliable way to distinguish a bare word being used as a key from a bare word that is an identifier reference, a keyword, or a value. By requiring quotation marks, JSON eliminates this ambiguity entirely — the parser always knows that a quoted string before a colon is a property name.
JavaScript object literal syntax is specifically designed for embedding data directly in JavaScript source code, where the surrounding JavaScript grammar provides context. JSON is designed for language-neutral data interchange, where the text must be parseable without any language-specific context. The requirement for quoted keys is what makes JSON parseable in Python, Ruby, Go, Java, Rust, and every other language using identical grammar rules.
A historical footnote: early implementations of JSON parsers sometimes accepted unquoted keys as a permissive extension. This behavior was never part of the specification and was removed from compliant parsers. Today, compliant JSON parsers in all major languages strictly require double-quoted keys, and the error messages they produce clearly identify the position of the first unquoted character.
Reading the error position to find unquoted keys
The key to diagnosing unquoted key errors quickly is reading the error position number included in the message. Node.js v18 and later prints 'SyntaxError: Expected double-quoted property name in JSON at position N' where N is the character offset from the start of the string at which the parser encountered the unexpected token. If the position is 1, the parser got past the opening brace and immediately found an unquoted character. If the position is larger, count N characters into the JSON string to find the offending key.
For Python, the json.JSONDecodeError message format is 'Expecting property name enclosed in double quotes: line L column C (char N)'. Python gives you both the line and column numbers, which is more helpful for multi-line JSON. Line 1 column 2 means the very first key is unquoted. Use the line and column to jump directly to the offending location in the text.
For jq, the error message is less specific: 'parse error (Invalid numeric literal at line L, column C)'. This message appears because jq tries to interpret the unquoted key as a number before failing. Despite the message mentioning 'numeric literal', the cause is almost always an unquoted string key, not an actual number. The line and column from jq's message still point to the correct location.
The fastest diagnosis tool is to paste the text into ToolDock JSON Validator. It parses the text and highlights the exact character position of the error with a visual indicator. For large JSON bodies, this is significantly faster than counting character offsets manually. Once you find the first unquoted key, search the rest of the text for the same pattern — there are typically multiple unquoted keys in a JavaScript object literal that was mistakenly treated as JSON.
Converting JavaScript objects to valid JSON strings
The most reliable fix for the source of unquoted keys is to produce JSON using JSON.stringify rather than constructing JSON text manually or by copying JavaScript object syntax. JSON.stringify takes any serializable JavaScript value and returns a valid JSON string with all keys double-quoted and all string values double-quoted. It handles nested objects, arrays, null, numbers, and booleans correctly without any manual quoting.
For a one-time fix when you have a JavaScript object literal string that you need to parse, the cleanest solution is to move the data into a proper JavaScript object literal in your code and then call JSON.stringify if you need to send it as JSON text, or simply use the JavaScript object directly without JSON round-tripping it at all. Many developers reach for JSON.parse when they have data that is already available as a JavaScript object, which is unnecessary — the extra parse step adds both a performance cost and a correctness risk.
If you receive JSON text from an external source — an API, a file, a database — and it contains unquoted keys, the text is not valid JSON and the source must be fixed. You cannot reliably convert unquoted keys to quoted keys with a regular expression because the regex would also match identifiers inside string values, potentially corrupting the data. The correct fix is at the source: update the code that produces the text to use JSON.stringify or a proper JSON serialization library.
For JavaScript configuration files that are read with fs.readFileSync and then parsed, the correct solution is to either change the file extension to .json and convert the content to valid JSON format, or to use require() for .js files which executes the module correctly. Node.js's built-in import() for ESM also handles .json files natively without requiring manual readFileSync and JSON.parse. For environments that support it, the JSON import syntax (import config from './config.json' assert {type: 'json'}) is the cleanest approach.
Python json.JSONDecodeError versus Node SyntaxError
The error messages from different JSON parsers for the same unquoted key input are notably different in wording but consistent in pointing to the same location in the source text. Understanding these differences speeds up cross-language debugging.
Python's json module produces json.JSONDecodeError with the message 'Expecting property name enclosed in double quotes'. This message is the clearest of all major JSON parsers — it directly states what is missing. The exception carries .lineno and .colno attributes alongside .doc and .pos for the full context. Python also inherits from ValueError, so catching ValueError catches JSONDecodeError for code that does not need to distinguish the specific error type.
Node.js has changed its JSON error messages across versions. In Node.js v16 and earlier, the message was the less descriptive 'Unexpected token n in JSON at position 1' where 'n' was the first character of the unquoted key — the letter 'n' in {name: value}. This message was confusing because 'unexpected token n' suggests something related to 'n', not a missing quote. In Node.js v18 and later with V8 version 10.1+, the message changed to the more helpful 'Expected double-quoted property name in JSON at position 1', directly mirroring Python's clarity.
Firefox's JSON parser (SpiderMonkey) produces 'JSON.parse: expected property name or \'}\' at line 1 column 2 of the JSON data'. Safari and Chrome both use V8 so they produce the V8 message. This matters when debugging JSON errors reported from browsers — error messages in user-submitted bug reports or telemetry vary by browser.
For Go, the encoding/json package produces 'invalid character \"k\" looking for beginning of object key string' where 'k' is the first character of the unquoted key. Go's message is similar in clarity to Python's updated V8 message. In Rust with serde_json, the error is 'key must be a string' with a line and column reference. All of these parsers implement RFC 8259 correctly — the grammar is the same and the only variation is how each parser words its diagnostic message.
Configuration files written as JS objects passed to JSON.parse
The most common real-world occurrence of the unquoted key error involves configuration files. JavaScript projects accumulate configuration in files like webpack.config.js, babel.config.js, jest.config.js, .eslintrc.js, and many others. These files use JavaScript module syntax and object literal notation, which allows unquoted keys. They are perfectly valid as JavaScript modules loaded with require() or import().
The error occurs when code treats these files as JSON. A script that reads a config file with fs.readFileSync and calls JSON.parse on the text will fail immediately if the file uses JavaScript syntax. The script might have been written for a .json config file and later pointed at a .js config file without updating the parsing code. It might also be a generic configuration loader that attempts to detect the format but falls back to JSON.parse incorrectly.
Another common scenario involves JSON5 format. JSON5 is a community extension of JSON that explicitly allows unquoted keys, single-quoted strings, trailing commas, and comments. Some projects use JSON5 for their configuration because it is friendlier for human-written files. JSON5 files often use the .json5 extension, but sometimes they use .json or no extension, leading downstream code to pass them to a standard JSON.parse that does not understand JSON5 syntax. The fix is to use the json5 npm package for JSON5 parsing rather than the native JSON.parse.
A third scenario involves database-stored JSON that was entered manually by an operator or exported from a tool that produces JavaScript-like output rather than strict JSON. MongoDB's shell, for example, accepts JavaScript syntax in queries. Export tools that read MongoDB shell scripts and then feed the output to a JSON parser encounter unquoted keys. The solution is to use the mongodump/mongoexport tools which produce valid JSON output, or to apply JSON.stringify to the JavaScript objects in the shell before exporting.
Editor settings that highlight unquoted JSON keys
Preventing unquoted key errors before they reach a parser is significantly easier than debugging them after the fact. Modern code editors offer JSON validation as a built-in feature that highlights syntax errors including unquoted keys in real time as you type.
In VS Code, files with the .json extension receive automatic JSON syntax validation. The editor underlines any syntax error with a red squiggly line and shows the error in the Problems panel. Hovering over the underlined text displays the error message. This catches unquoted keys immediately when editing JSON files. However, files with .jsonc extension allow comments (JSON with Comments), and files with no extension that contain JSON-like content do not receive automatic validation — you must add 'json' to the language mode selector in the status bar.
For VS Code specifically, the editor also supports enabling JSON validation for files that are not .json by adding language associations to your settings.json: 'files.associations': {'*.config': 'json'} forces JSON validation on .config files. For project-specific settings, this configuration can go in .vscode/settings.json and is committed to the repository so all team members benefit from the same editor validation.
JetBrains IDEs (IntelliJ IDEA, WebStorm, PyCharm) provide JSON inspection that highlights structural errors including unquoted keys. The inspection runs on any file identified as JSON through its extension or through the file type association settings. Both editors also provide a JSON reformatter that adds missing quotes to keys when reformatting, though relying on the formatter to fix invalid JSON is less reliable than producing valid JSON from the start.
For automated enforcement in CI, running jsonlint or ajv on all .json files in the repository as part of the lint step catches any unquoted key before it is merged. A simple check like find . -name '*.json' -exec jsonlint {} + in a CI step prevents invalid JSON from entering the codebase. For projects that also use JSON Schema, validating all JSON files against their schemas in CI catches both syntax errors and semantic contract violations simultaneously.
Quick fix checklist
- ✓Paste the JSON text into ToolDock JSON Validator — it highlights the exact position of the first unquoted key
- ✓Read the error position number and count characters from the start of the text to find the unquoted key
- ✓Use JSON.stringify on a JavaScript object to produce valid JSON rather than writing JSON text manually
- ✓Use require() or import() for .js config files — do not use fs.readFileSync plus JSON.parse on JavaScript module files
- ✓Rename config files to .json and convert their syntax if they need to be parsed with JSON.parse
- ✓Install the json5 npm package if your files use JSON5 format — do not pass JSON5 text to native JSON.parse
- ✓Enable JSON validation in your code editor for all files with .json extension to catch unquoted keys while typing
- ✓Add a jsonlint or JSON schema validation step to your CI pipeline to block invalid JSON from being merged
Related guides
Frequently asked questions
Why does JSON require double quotes around keys?
RFC 8259, the JSON specification, defines object names as strings, and strings must use double-quote delimiters (U+0022). This requirement exists to make JSON parseable in any language without ambiguity. A bare identifier in a key position is valid JavaScript syntax but requires JavaScript grammar knowledge to parse correctly. Double quotes make JSON self-describing and parseable by any language's JSON parser without ambiguity.
What is the exact error message for an unquoted JSON key?
In Node.js v18+: 'SyntaxError: Expected double-quoted property name in JSON at position N'. In Node.js v16 and earlier: 'SyntaxError: Unexpected token X in JSON at position N' where X is the first character of the unquoted key. In Python: 'json.JSONDecodeError: Expecting property name enclosed in double quotes: line L column C'. In Firefox: 'JSON.parse: expected property name or \'}\''. All point to the character offset where the unquoted key begins.
Can I use single quotes for JSON keys?
No. The JSON specification requires double quotes (U+0022) for both keys and string values. Single quotes are valid in JavaScript string literals but are explicitly not part of the JSON grammar. JSON.parse throws SyntaxError for any single-quoted key or value. If you need to store JSON with single quotes, convert the text to use double quotes before parsing — either by fixing the source or using a tool that converts JavaScript object notation to JSON.
How do I convert a JavaScript object literal to valid JSON?
Build the data as a JavaScript object using normal object literal syntax — unquoted keys and all — then call JSON.stringify(yourObject) to produce a valid JSON string with all keys properly double-quoted. JSON.stringify handles nested objects, arrays, null, numbers, and booleans. Do not attempt to convert object literal text to JSON using string replacement or regular expressions — the conversion is unreliable and risks corrupting string values.
Why does jq say 'Invalid numeric literal' for an unquoted key?
jq's parser attempts to interpret an unquoted value as a number before deciding it is invalid. When it encounters the first character of an unquoted key like {host: 'value'}, it reads 'h' and tries to parse it as a numeric literal, which fails immediately. The error message reflects jq's internal parsing sequence rather than the semantic cause. The actual problem is the unquoted key, not any number. Quote the key and the jq error resolves.
How do I read a JavaScript config file in Node.js without JSON.parse?
Use require('./config.js') for CommonJS modules — require executes the JavaScript file and returns the module's exported value. For ESM, use const config = await import('./config.js'). Both approaches correctly handle JavaScript object literal syntax. Only use JSON.parse for files that contain valid JSON text. If your config file uses .json extension, use require('./config.json') or fs.readFileSync followed by JSON.parse.
What is JSON5 and how is it different from JSON?
JSON5 is a community extension of JSON that allows unquoted keys, single-quoted strings, trailing commas, line and block comments, and hexadecimal numbers. JSON5 files typically use the .json5 extension. Standard JSON.parse cannot parse JSON5 — use the json5 npm package for Node.js, which provides a compatible API. JSON5 is designed for human-authored configuration files where strict JSON syntax is inconvenient but machine-generated data exchange still uses standard JSON.
Can a linter catch unquoted JSON keys automatically?
Yes. Running jsonlint on .json files catches unquoted keys immediately with a clear error message. VS Code's built-in JSON language support underlines unquoted keys with a red squiggly line in real time. Adding a jsonlint check to your CI pipeline prevents any file with unquoted keys from being merged. For projects using JSON Schema, ajv with schema validation also catches syntax errors before semantic validation even runs.
Why does the browser console show objects with unquoted keys?
Browser developer tools display JavaScript objects using JavaScript object notation, which shows keys without quotes for readability. This display format is not JSON — it is the JavaScript debugger's representation of the in-memory object. If you copy the console output and paste it into a JSON.parse call or a JSON file, it will fail because of the unquoted keys. Always use JSON.stringify(object, null, 2) to get properly formatted JSON text from a JavaScript object.
All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-05.