---
title: Two Sessions, One Identifier
type: anti-pattern
level: L2
status: live
revision: 1
updated: 2026-08-14
systemVersion: 4.2
tags: [data-integrity, concurrency, identifiers]
rating: 8.35
ratingAxes: useful 8 · evidence 9 · pull 8 · original 8 · form 9
ratingKind: derived
source: controlling ledger duplicate IDs, 2026-08-14
---

# Two Sessions, One Identifier

_Written 2026-08-14 · last verified 2026-08-14 · system v4.2 · live_

**TL;DR** — Two concurrent sessions wrote records under the same identifier within minutes. Both had taken the next number from a summary line at the top of the file rather than from the highest number actually present. Allocating an identifier from a cached count instead of the live maximum breaks the moment anything runs in parallel.

## Pattern

Records in a ledger are numbered sequentially. To add one, you read the header — *highest ID: 51* — and write 52.

Two runs do this minutes apart. Both write 52. One ledger ended up with 34 record blocks under 33 unique identifiers. Now one identifier points at two different findings, and every reference to it is ambiguous.

## Why it looks right

The header exists precisely so nobody has to scan the whole file. Reading it is faster, and it was correct at the moment it was written.

Single-threaded, this works for years. The bug is invisible until something runs twice at once — and by then the practice is established everywhere.

## Why it fails

The header is a **cache of a fact that lives in the body**. Any cache read without a write lock is a race, and the window is as long as the gap between reading and writing, which for a human-paced process is minutes rather than milliseconds.

Recovery is the expensive part. Renumbering a fresh duplicate is mechanical. Renumbering an old one means finding every cross-reference in every other file that cites the number — and if any reference is missed, it now silently points at the wrong record.

## Instead

**Allocate from the live maximum, not from a summary.** Grep the body for the highest identifier immediately before writing:

> `grep -o '^### CTRL-[0-9]*' ledger.md | sort | tail -1`

Two further rules earn their keep. Make identifiers cheap to keep unique — a timestamp or a short random suffix removes the race entirely at the cost of prettiness. And when you do find a duplicate, **fix the young one and leave the old one flagged**: the young record has no inbound references yet, the old one may have many, and a mechanical renumber of the old one converts a visible collision into a set of silent misdirections.
