OSM Feature Identity & ID Stability Jump to heading
An OpenStreetMap identifier is a name for a database object, not for a thing in the world. Most pipelines assume otherwise, and the assumption holds until somebody splits a road.
The Problem This Topic Solves Jump to heading
Your pipeline stores references to OSM features — a conflation match, a cached geocode, a curated list of interesting objects, an enrichment join key — and needs them to still be right next month. Whether they are depends entirely on what an identifier guarantees, which is less than most people assume.
The failure scenario is quiet. A team stores way identifiers for a curated set of cycle routes. Six months later a mapper splits several of those ways at junctions, which is routine and correct mapping. Every stored identifier still resolves, so nothing errors — but each now names a fragment of the route it used to name, and the pipeline’s length statistics quietly drop by forty percent. Nobody notices until somebody compares against an external figure.
What an Identifier Actually Guarantees Jump to heading
Four properties, and it is worth being precise about each.
Identifiers are scoped by type. A node, a way and a relation can all carry the identifier 12345, and they are three unrelated objects. The unique key is the pair, which is why every table in this section carries a composite key rather than a bare number.
Identifiers are not reused after deletion. A deleted object’s identifier is retired, so a stored reference to a deleted feature resolves to nothing rather than to something else. That is a genuinely useful guarantee and it is why deletion is the least dangerous of the four edits above.
Versions increment on every edit. The version number tells you the object changed; it does not tell you how or whether the change matters to you. A version bump from a typo fix and one from a complete retagging look identical.
Nothing guarantees the object still means the same thing. An identifier names a database row. What that row represents can change entirely — a building becoming a shop, a road being re-purposed, a boundary being redrawn — with no signal beyond a version increment.
The Four Edits That Break Assumptions Jump to heading
Retagging is benign for identity: the identifier and the geometry are unchanged, and only the meaning may have moved. A stored reference still names the same object.
Splitting is the dangerous one. When a way is split, one part conventionally keeps the original identifier and the others are new objects. A stored reference still resolves, silently, to a shorter feature. Nothing errors; statistics simply change.
Merging deletes one identifier and keeps another. Stored references to the deleted one stop resolving, which at least fails loudly.
Replacement happens when a feature’s representation changes form — a closed way becoming a multipolygon relation is the common case. The old identifier is deleted and a new object of a different type appears. A stored reference resolves to nothing, and a pipeline that only looked at ways never sees the replacement.
Where Identity Assumptions Hide Jump to heading
Identity assumptions rarely announce themselves. They sit inside code that reads perfectly well and only becomes wrong when the map moves. Four places are worth auditing in any existing pipeline.
Join keys. Any join on a bare numeric identifier is assuming uniqueness that does not hold across element types. The symptom is a result set larger than expected, which looks like a legitimate many-to-many relationship rather than like a bug.
Cached geometry. A stored polygon or line keyed on an identifier is assuming the geometry has not changed. That assumption is usually fine for a building and routinely wrong for a road, and nothing in the cache indicates which kind it holds.
Curated lists. A hand-assembled set of interesting features — the routes a product covers, the sites a report describes — is the highest-value and most fragile identity assumption in most pipelines, because it was built by a human who will not be watching it.
Aggregate comparisons across time. Any figure compared against the same figure last quarter assumes the underlying features are the same features. A split changes the count without changing anything on the ground, and a comparison that does not account for that reports a change that did not happen.
The common thread is that none of these produce an error. Each produces a plausible number, which is why the audit has to be deliberate rather than driven by incidents — by the time an incident occurs, the wrong numbers have usually been reported for months.
Building Something That Survives Jump to heading
Three techniques, in increasing order of effort and robustness.
Store the version alongside the identifier. A stored reference becomes a pair, and re-resolving compares the current version against the stored one. That does not tell you what changed, but it tells you that something did, which turns a silent drift into a review item. It is the cheapest possible improvement and it catches the split case, because the surviving part’s version increments.
Store enough to re-verify. A coordinate, a name, a length — whatever the reference was for. Re-resolving then checks not just that the object exists but that it still looks like what you stored. A route whose length halved is a re-verification failure even though the identifier resolved.
Prefer an external identifier where one exists. A ref:* tag, an operator’s code or a knowledge-base link survives splits and replacements, because it describes the thing rather than the database row. Linking OSM Features to Wikidata Identifiers covers establishing those deliberately, and they are the only references in this section that are genuinely durable.
For internal purposes, a surrogate key — a stable identifier your pipeline mints and maps to OSM objects — decouples your data model from the map’s churn entirely. Building Stable Surrogate Keys for OSM Features develops it.
What to Store, Concretely Jump to heading
A stored reference that survives ordinary editing carries five fields, and each earns its place.
The element type and identifier together form the key. The version at capture time makes any later change detectable. A verifiable attribute — whichever property the reference exists to describe — makes a split or a retagging detectable rather than merely a change. And the capture date explains drift when somebody asks why a figure moved, because it establishes what the map looked like when the reference was made.
That is five small columns. The temptation to store only the identifier is strong precisely because it works: everything resolves, nothing errors, and the cost appears months later as a number nobody can explain.
Validation and Error Handling Jump to heading
| Condition | Root cause | Detection | Remediation |
|---|---|---|---|
| Statistics drop with no code change | Ways split upstream | Aggregate measures fall; ids still resolve | Store versions and re-verify attributes |
| Joins produce duplicates | Joined on a bare numeric identifier | Same number across element types | Key on the type and identifier pair |
| Reference resolves to nothing | Object deleted or replaced | Not-found on re-resolution | Expected; route to review rather than dropping |
| A way became a relation | Representation changed form | Identifier gone, similar feature nearby | Search by attributes, not only by identifier |
| Stale meaning, current identifier | Retagged into something else | Attributes no longer match what was stored | Re-verify stored attributes, not just existence |
| Nothing ever re-checked | References stored and never revisited | Quality decays invisibly | Re-resolve on a schedule tied to edit rates |
| History unavailable for a fix | Only current state retained | Cannot see what the object used to be | Consult a history file for the prior state |
Re-Resolution as a Scheduled Job Jump to heading
References decay, and the only reliable response is to re-resolve them on a schedule. Two design points make that affordable.
Batch by area, not by record. Re-resolving a thousand scattered identifiers is a thousand requests; re-resolving everything in one region is one extract read. For any volume, the file-based route from Choosing Between Overpass and a Local Extract is the right one.
Drive the schedule from edit rates. An area with heavy mapping activity needs re-resolution far more often than a stable one, and the replication stream already tells you which is which. Using the diff stream to mark regions as dirty — the pattern in Incremental Updates for Derived Datasets — turns a blanket weekly sweep into targeted work.
Guides in This Topic Jump to heading
- Tracking an OSM Feature Across Versions — following an object through its edit history and classifying what each version changed.
- Building Stable Surrogate Keys for OSM Features — minting your own identifiers and mapping them to a moving map.
- Handling Deleted and Redacted OSM Objects — the difference between a deletion and a redaction, and why it matters downstream.
Frequently Asked Questions Jump to heading
Are OSM identifiers unique on their own?
No. They are scoped by element type, so a node, a way and a relation can each carry the same number while being entirely unrelated objects. The unique key is the pair of type and identifier, and joining on the bare number produces phantom duplicates that are easy to miss because they look like genuine multiple matches rather than like an error.
Are identifiers reused after an object is deleted?
No, and this is one of the few genuinely strong guarantees available. A deleted object’s identifier is retired, so a stored reference to it resolves to nothing rather than to some unrelated feature that inherited the number. That makes deletion the least dangerous of the ways a stored reference can go wrong, because it fails loudly and unambiguously.
What happens to my stored reference when a way is split?
It keeps resolving, to a shorter way. Conventionally one part retains the original identifier and the other parts become new objects, so nothing errors and nothing indicates that the feature you stored is now a fragment. This is the most damaging identity failure precisely because every check a naive pipeline performs passes. Storing the version and an attribute such as length, and re-verifying both, is what catches it.
Does a version increment tell me what changed?
Only that something did. A version bump from a corrected spelling and one from a complete retagging are indistinguishable from the number alone. Comparing versions is still worthwhile because it converts silent drift into a review item, but deciding whether the change matters requires either comparing the stored attributes against the current ones or fetching the previous version and diffing them.
Is there any genuinely durable reference to an OSM feature?
An external identifier, where one exists — a published reference code, an operator’s own identifier, a knowledge-base link. Those describe the thing in the world rather than the database row, so they survive splits, merges and representation changes that retire an OSM identifier. Establishing such links deliberately is more work up front and is the only approach that does not require periodic re-resolution.
Related Jump to heading
- OSM Data Fundamentals & Architecture — the parent section and the element model identifiers name.
- Node, Way & Relation Data Model — what each identifier type refers to.
- OSM Conflation & Data Enrichment — where stored references decay most visibly.
- Full History .osh.pbf Processing — the data that lets you see what an object used to be.
- Reconstructing OSM Features at a Past Date — recovering a prior state when a reference breaks.
- The OSM Editing API & Changeset Upload — where version semantics become a concurrency mechanism.
Up one level: OSM Data Fundamentals & Architecture.