JuliusBrussee/caveman · error · ValueError

duplicate

Error message

duplicate

What it means

While parsing a JSON object, walk decodes each key with raw_decode and requires it to be a string and not already present in the result dict. Since Python's json module normally keeps last-duplicate silently, this parser intentionally rejects duplicate keys to preserve lossless span tracking.

Solutions

  1. Deduplicate keys in the JSON source so each object has unique key names
  2. Pre-validate the text with json.loads(object_pairs_hook=list) and check for repeated keys
  3. Fix the producer that emits duplicate keys (templating or merge bug)
  4. If last-wins semantics are desired, normalize via stdlib json.loads before passing to parse

Example fix

# before
text = '{"branch": "a", "branch": "b"}'
value = parse(text)  # ValueError: duplicate
# after
text = '{"branch": "b"}'  # keep only the intended key
value = parse(text)
Defensive patterns

Strategy: validation

Validate before calling

import json
def has_no_duplicate_keys(text):
    seen = []
    def hook(pairs):
        keys = [k for k, _ in pairs]
        if len(keys) != len(set(keys)):
            raise ValueError("duplicate keys")
        return dict(pairs)
    json.loads(text, object_pairs_hook=hook)
    return True

Try / catch

try:
    value = parse(text)
except ValueError as e:
    if str(e) == "duplicate":
        raise ValueError("JSON object contains duplicate keys; deduplicate before parsing") from e

Prevention

When it happens

Trigger: Parsing a JSON object text that contains two identical keys (e.g. {"a":1,"a":2}) or a non-string key token, through parse().

Common situations: Hand-edited or concatenated JSON fragments producing duplicate keys; server responses built by naive string templating; migrating from stdlib json (which tolerates duplicates) to this strict wire parser.

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/46cb7b52982381b9. Report an issue: GitHub.

Appendix: source

Thrown at packages/middleware/python/caveman_middleware/_google_wire.py:37

            raise ValueError("bounded")
        white()
        start = index
        if text[index] == '"':
            value, index = decoder.raw_decode(text, index)
            strings[path] = (start, index, value)
            return value
        if text[index] == "{":
            index += 1
            result = {}
            white()
            if text[index] == "}":
                index += 1
                return result
            while True:
                white()
                key, index = decoder.raw_decode(text, index)
                if type(key) is not str or key in result:
                    raise ValueError("duplicate")
                white()
                if text[index] != ":":
                    raise ValueError("colon")
                index += 1
                result[key] = walk((*path, key), depth + 1)
                white()
                end = text[index]
                index += 1
                if end == "}":
                    return result
                if end != ",":
                    raise ValueError("comma")
        if text[index] == "[":
            index += 1
            result = []
            white()
            if text[index] == "]":
                index += 1

View on GitHub (pinned to 3ee70a1026)