JuliusBrussee/caveman · error

object not closed

Error message

object not closed

What it means

After consuming an object's fields, readJSONTOONValue reads one more token and requires it to be the '}' delimiter; anything else raises "object not closed". This enforces well-formed JSON objects so the TOON conversion is lossless and never guesses at boundaries.

Solutions

  1. Validate the input JSON with `jq empty input.json` before running the TOON verb.
  2. Ensure the input stream is complete (check the producer didn't truncate the file/pipe).
  3. Strip comments/trailing commas — feed strict RFC 8259 JSON only.
  4. If you changed the parser, verify token consumption per branch matches the standard decoder's contract.

Example fix

// before: truncated
echo '{"a": [1,2' | caveman-engine toon
// after
echo '{"a": [1,2]}' | caveman-engine toon
Defensive patterns

Strategy: validation

Validate before calling

// strict pre-check with the stdlib
var probe any
if err := json.Unmarshal(input, &probe); err != nil { return err } // catches truncation/mismatched braces

Try / catch

v, err := parseJSONTOON(input)
if err != nil && strings.Contains(err.Error(), "object not closed") {
    return fmt.Errorf("truncated or malformed JSON object: %w", err)
}

Prevention

When it happens

Trigger: In the object branch of readJSONTOONValue, the closing dec.Token() returns a non-Delim token or a Delim other than '}' — e.g. truncated JSON, a stray ']' instead of '}', or a tokenizer that stopped early.

Common situations: Truncated JSON from a cut-off pipe or partial file; mismatched braces from string-assembled JSON; feeding JSON5/JSONC (comments, trailing commas) that the strict parser mishandles; a bug in a rewritten parser loop.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/65044ea2f5ba712b. Report an issue: GitHub.

Appendix: source

Thrown at engine/compressors/toon_encode.go:158

				if !ok {
					return toonValue{}, fmt.Errorf("object key is %T", keyTok)
				}
				if _, duplicate := seen[key]; duplicate {
					return toonValue{}, fmt.Errorf("duplicate object key %q", key)
				}
				seen[key] = struct{}{}
				val, err := readJSONTOONValue(dec)
				if err != nil {
					return toonValue{}, err
				}
				fields = append(fields, toonField{name: key, value: val})
			}
			end, err := dec.Token()
			if err != nil {
				return toonValue{}, err
			}
			if d, ok := end.(json.Delim); !ok || d != '}' {
				return toonValue{}, fmt.Errorf("object not closed")
			}
			return toonValue{kind: toonObject, obj: fields}, nil
		case '[':
			var arr []toonValue
			for dec.More() {
				val, err := readJSONTOONValue(dec)
				if err != nil {
					return toonValue{}, err
				}
				arr = append(arr, val)
			}
			end, err := dec.Token()
			if err != nil {
				return toonValue{}, err
			}
			if d, ok := end.(json.Delim); !ok || d != ']' {
				return toonValue{}, fmt.Errorf("array not closed")
			}

View on GitHub (pinned to 3ee70a1026)