Documents / Issues / issue-413546da5db7
A frontmatter list naming one entry twice wedged reindex or refused --replace-body
Why a repeated tag failed every reindex, why a repeated glob or edge made --replace-body refuse the document for good, and where the repeat is now dropped.
Symptom¶
A frontmatter list that names one entry twice left the file and its index disagreeing, and no command docir offers could bring them back together.
- A tag (
tags: [x, x]).docir addorupdatewrote the file, then failed with a rawsqlite3.IntegrityError: UNIQUE constraint failed: document_tags.doc_id, document_tags.tag_key, exit 1. Everydocir reindexafter it failed the same way, so CI'sreindex -> doctor --strict -> check --strictstopped at its first step.docir doctorreportedindex-behind-filesand pointed atdocir check, which named nothing — the file parses. - A code glob or an identical edge. The index kept one and the file two, so
the two hashed differently, and
update --replace-bodyrefused the document with exit 6, "changed on disk since it was indexed". Refetching re-read the same disagreement, so the refusal was permanent.
Measured on scratch stores, 2026-09-24.
How a repeat reaches a file¶
The CLI drops a repeat before the write (issue-2bb30af6216d), so --tags x,x no
longer gets here. Everything else still can: the MCP docir_add/docir_update
tools take a list and pass it through, a hand edit or a merge of two branches
that each added the entry writes one, and so does a file left by an earlier
build.
Resolution¶
Document drops a repeated tag, glob or identical edge when it is built,
keeping the first. Every path builds one — parsing a file, add and update
on either transport, tag rename — so the index, content_hash and every file
docir writes agree. A file that already holds a repeat reads without it,
reindexes, takes --replace-body, and loses the repeat on its next write.
The file is then the only place a repeat is still visible, so docir check
reads it there: repeated-entry, a warning naming the file and what it repeats,
and docir check --fix rewrites the file without it and leaves updated alone.
An edge is compared by what it says — adr-1 and {to: adr-1} repeat — and two
kinds to one target are not a repeat: which one the author meant is a judgement.
A build that cannot read this fix still fails on a repeated tag. The packaged troubleshooting reference says how to find the file and what to delete.