twentyhq/twenty · error · ValueError
Duplicate key found in JSON: '{key}'
Error message
Duplicate key found in JSON: '{key}' What it means
Raised by the object_pairs_hook `check_duplicate_keys` passed to json.load when parsing the replacement JSON in replace.py. Because Python's default dict silently overwrites duplicate keys, the hook makes a duplicate key a hard error so a replacement file cannot accidentally shadow an earlier mapping for the same shape/slide.
Source
Thrown at packages/twenty-server/src/engine/core-modules/code-interpreter/sandbox-scripts/pptx/replace.py:209
first_text += "..."
unused_with_content.append(f"{k} ('{first_text}')")
else:
unused_with_content.append(k)
errors.append(
f"Shape '{shape_key}' not found on '{slide_key}'. "
f"Shapes without replacements: {', '.join(sorted(unused_with_content)) if unused_with_content else 'none'}"
)
return errors
def check_duplicate_keys(pairs):
"""Check for duplicate keys when loading JSON."""
result = {}
for key, value in pairs:
if key in result:
raise ValueError(f"Duplicate key found in JSON: '{key}'")
result[key] = value
return result
def apply_replacements(pptx_file: str, json_file: str, output_file: str):
"""Apply text replacements from JSON to PowerPoint presentation."""
# Load presentation
prs = Presentation(pptx_file)
# Get inventory of all text shapes (returns ShapeData objects)
# Pass prs to use same Presentation instance
inventory = extract_text_inventory(Path(pptx_file), prs)
# Detect text overflow in original presentation
original_overflow = detect_frame_overflow(inventory)
# Load replacement data with duplicate key detectionView on GitHub (pinned to 1f5dd2bbd2)
Solutions
- Open the replacement JSON and find the duplicated key named in the message; merge or rename the conflicting entry.
- Validate the JSON with a linter that flags duplicate keys (e.g. `python -c` using the same object_pairs_hook) before passing it to apply_replacements.
- When generating replacements programmatically, accumulate into a dict so later writes overwrite deterministically, then serialize once.
Example fix
// before — replacement.json has two "slide-1" keys
{
"slide-1": { "title": "A" },
"slide-1": { "subtitle": "B" }
}
// after — merged under one key
{
"slide-1": { "title": "A", "subtitle": "B" }
} Defensive patterns
Strategy: validation
Validate before calling
import json
def load_replacements_strict(path: str) -> dict:
with open(path) as f:
return json.load(f, object_pairs_hook=check_duplicate_keys)
# Pre-flight: lint the file before the tool runs
try:
load_replacements_strict('replacements.json')
except ValueError as e:
print(f'Fix duplicate keys first: {e}')
raise Type guard
def has_no_duplicate_keys(pairs) -> bool:
seen = set()
for k, _ in pairs:
if k in seen:
return False
seen.add(k)
return True Try / catch
try:
replacements = json.load(f, object_pairs_hook=check_duplicate_keys)
except ValueError as e:
if 'Duplicate key' in str(e):
# surface the key name and the file path to the operator, then abort
raise ValueError(f'{path}: {e}. Merge or rename the duplicate before retrying.') from e
raise Prevention
- Generate replacement JSON by building a dict in memory and serializing once — later writes overwrite deterministically with no duplicates emitted.
- Lint replacement files with the same object_pairs_hook before passing them to apply_replacements.
- When merging two replacement drafts, dedupe by key before serializing.
When it happens
Trigger: The replacement JSON contains the same key twice at the same nesting level — e.g. two `slide-1` entries, or two shape keys under the same slide. This typically comes from hand-editing or from an LLM concatenating partial replacement blocks without dedup.
Common situations: An LLM in the code-interpreter merges two inventory-driven replacement drafts into one JSON and forgets to dedupe; a templating step emits a key per source without grouping; copy-paste of a shape block that retains the original key.
Related errors
- Slide index {idx} out of range (0-{total_slides - 1})
- Found {len(errors)} validation error(s)
- Found {len(overflow_errors)} overflow error(s) and {len(warn
- PDF conversion failed
- Image conversion failed
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/01bbd8422cee2e5e.
Report an issue: GitHub.