antonmedv/fx · error

Expected object key to be a string, got %q

Error message

Expected object key to be a string, got %q

What it means

This error is thrown by the JSON parser while parsing an object: after '{' or a comma, it expects the next token to be a quoted string serving as the object key. If the current character is anything other than '"', the parser panics with this message, echoing the offending character. It fires because JSON grammar only permits string keys in objects.

Source

Thrown at internal/jsonx/json.go:336

		Depth:      p.depth,
		LineNumber: p.lineNumberPlusPlus(),
	}
	object.Value = curlyBracketOpen

	p.next()
	p.skipWhitespace()

	// Empty object
	if p.char == '}' {
		object.Value = curlyBracketPair
		p.next()
		return object
	}

	for {
		// Expecting a key which should be a string
		if p.char != '"' {
			panic(fmt.Sprintf("Expected object key to be a string, got %q", p.char))
		}

		keyBytes := p.scanString()

		p.skipWhitespace()

		// Expecting colon after key
		if p.char != ':' {
			panic(fmt.Sprintf("Expected colon after object key, got %q", p.char))
		}

		p.next()

		p.depth++
		value := p.parseValue(false)
		value.Key = keyBytes
		value.Parent = object
		p.depth--

View on GitHub (pinned to 4f31cd3a0c)

Solutions

  1. Quote the object key with double quotes: {foo:1} -> {"foo":1}
  2. Replace single quotes with double quotes: {'a':1} -> {"a":1}
  3. Remove stray commas or characters before the key (e.g. {,} -> {})
  4. Validate the JSON with a linter/formatter (jq, JSONLint) before feeding it to the parser

Example fix

// before
{"name": "x", "enabled": true}
// given input: {name: "x"}

// after
{"name": "x"}
Defensive patterns

Strategy: validation

Validate before calling

func validateObjectKeys(src string) error {
	// reject unquoted keys before parsing: look for '{' or ',' followed by non-'"', non-'}' content
	inStr := false
	for i := 0; i < len(src); i++ {
		c := src[i]
		if c == '"' { inStr = !inStr; continue }
		if inStr { continue }
		if c == '{' || c == ',' {
			j := i + 1
			for j < len(src) && (src[j]==' '||src[j]=='\t'||src[j]=='\n'||src[j]=='\r') { j++ }
			if j < len(src) && src[j] != '"' && src[j] != '}' {
				return fmt.Errorf("object key at offset %d must be a quoted string, got %q", j, src[j])
			}
		}
	}
	return nil
}

Try / catch

// the library panics; recover at the API boundary
func safeParse(p *jsonx.JsonParser, data []byte) (root *jsonx.Node, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("json parse failed: %v", r)
		}
	}()
	root = p.Parse(data)
	return
}

Prevention

When it happens

Trigger: Parsing input like {foo:1} (unquoted key), {foo,:1}, {,}, {'a':1} (single quotes), or a truncated object where the key is missing entirely (e.g. '{' followed by end-of-input or whitespace then a non-quote char).

Common situations: Hand-written JSON config files with JavaScript-style unquoted keys; single-quoted keys copied from JS objects; template-generated JSON with missing placeholders; concatenation or truncation bugs producing partial JSON.

Related errors


AI-assisted analysis of antonmedv/fx@4f31cd3a0c (2026-09-02). Data as JSON: /api/errors/d6a34c64e4cfffd5. Report an issue: GitHub.