antonmedv/fx · error

Unexpected character %q in keyword

Error message

Unexpected character %q in keyword

What it means

parseKeyword matches literal keywords (true, false, null) character by character. If the character at position i does not match the expected letter of the keyword, the parser panics with "Unexpected character %q in keyword". This detects misspelled or truncated keywords like tru, nul, or falsy. The source region shows this fires inside the matching loop when p.char != name[i].

Source

Thrown at internal/jsonx/json.go:462

			}
			closeBracket.Value = squareBracketClose
			closeBracket.Parent = arr
			closeBracket.Index = -1
			arr.Append(closeBracket)
			p.next()
			return arr
		}

		panic(fmt.Sprintf("Invalid character %q in array", p.char))
	}
}

func (p *JsonParser) parseKeyword(name string, kind Kind) *Node {
	start := p.end - 1
	for i := 1; i < len(name); i++ {
		p.next()
		if p.char != name[i] {
			panic(fmt.Sprintf("Unexpected character %q in keyword", p.char))
		}
	}
	p.next()
	if isEndOfValue(p.char) {
		keyword := &Node{
			Kind:       kind,
			Depth:      p.depth,
			Value:      string(p.data[start : p.end-1]),
			LineNumber: p.lineNumberPlusPlus(),
		}
		return keyword
	}

	panic(fmt.Sprintf("Unexpected character %q in keyword", p.char))
}

func (p *JsonParser) parseNullOrNan() *Node {
	p.next()

View on GitHub (pinned to 4f31cd3a0c)

Solutions

  1. Spell the keyword exactly lowercase: true, false, null (not True, tru, Null)
  2. Fix case-sensitivity errors (True -> true)
  3. If a boolean/string was intended, quote non-keyword values: "True"
  4. Validate with a JSON linter before parsing

Example fix

// before
{"enabled": True}

// after
{"enabled": true}
Defensive patterns

Strategy: validation

Validate before calling

func validateKeywords(src string) error {
	// reject non-lowercase or misspelled literals outside strings
	tokens := []string{"true", "false", "null"}
	re := regexp.MustCompile(`(?<!["\w])([TtFfNn][A-Za-z]*)`)
	inStr := false
	for i := 0; i < len(src); i++ {
		if src[i] == '"' { inStr = !inStr; continue }
		if inStr { continue }
		rest := src[i:]
		loc := re.FindStringIndex(rest)
		if loc != nil && loc[0] == 0 {
			word := rest[loc[0]:loc[1]]
			ok := false
			for _, t := range tokens { if word == t { ok = true } }
			if !ok { return fmt.Errorf("invalid literal %q at offset %d (use lowercase true/false/null)", word, i) }
			i += loc[1] - 1
		}
	}
	return nil
}

Try / catch

func safeParse(p *jsonx.JsonParser, data []byte) (root *jsonx.Node, err error) {
	defer func() {
		if r := recover(); r != nil {
			if strings.Contains(fmt.Sprint(r), "in keyword") {
				err = fmt.Errorf("misspelled literal keyword: %v", r)
				return
			}
			err = fmt.Errorf("json parse failed: %v", r)
		}
	}()
	root = p.Parse(data)
	return
}

Prevention

When it happens

Trigger: Parsing inputs such as tru, treu, nul1, fals, or nullx's first mismatch char, e.g. 't' followed by anything other than 'r', entered parseKeyword via parseValue; also NaN handled separately in non-strict mode.

Common situations: Hand-edited JSON with typos in true/false/null; case mismatches like True/TRUE (JSON is case-sensitive); template glitches producing partial keywords; OCR or transcription errors in config.

Related errors


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