On this page
- How decisions are made
- The sources, and how far each is trusted
- A name is not an identifier
- The guards that stop bad merges
- The CAS cross-check: don't trust a second-hand cross-reference
- The formula check: ChEBI is not infallible either
- The primary-CAS rule: pick the compound that owns the number
- The curated-CAS gate: a vendor catalogue is not a registry
- The structure gate: a registry number need not denote a structure
- The stereochemistry gate: a flat registration is a statement
- Reading the structure, not the fingerprint
- How a stereochemistry difference is decided
- 281's warning about the standard flag that was always right, and it means a
- A gap is not a contradiction
- Restoring a stereochemistry no source holds
- When the registry breaks a tie between two sources
- An acid and its ion are two substances, not two shapes
- The cross-object synonym strip: a name belongs to one substance
- The catalogue strip: a product code is not a name
- When the evidence is not in the label
- Structure is computed, not asserted
- Salts: the formula and the structure do not say the same thing
- When the evidence is not enough
- Every value carries its provenance
How decisions are made
Harmonisation is a series of claims: this name belongs to this substance, this CAS number is correct, these two rows are the same flow. This page explains what evidence the project accepts for those claims, why several plausible-looking sources are not trusted on their own, and what happens when the evidence is not good enough.
If you want the step-by-step order in which these rules are applied, see Harmonisation steps.
The sources, and how far each is trusted
| Source | Used for | Trusted for identity? |
|---|---|---|
| EF 3.1 | The base flow list: names, CAS/EC, contexts, units, LCIA methods | Yes — it is the starting point |
| ChEBI | Synonyms, structures, cross-references | Yes, with a formula check |
| CAS Common Chemistry | Authoritative CAS ↔ name, structures | Yes — highest quality for CAS, but see A name is not an identifier |
| ECHA EC inventory | Official CAS ↔ EC correspondence | Yes, for that correspondence |
| PubChem | Structures, names, cross-references | Only when confirmed elsewhere |
| OPSIN | Parsing IUPAC names into structures | Yes, when the name parses unambiguously |
| RDKit | Deriving formula, mass, InChIKey from structure | Yes — it is computation, not assertion |
| GLAD mappings | EF 3.1 ↔ SimaPro flow correspondence | Yes, as a stated mapping |
| ecoinvent | An additional source list to merge | Yes, as a source list |
PubChem is the important exception. Its synonym and cross-reference data is crowd-sourced and contains errors, so it is never accepted as the sole basis for an identity claim. EF 3.1's own synonym lists appear to be drawn from PubChem, which is why they are not used either.
A name is not an identifier
Identifiers are ranked, and the ranking is not negotiable: a registry number outranks a name. A CAS number designates one substance by construction. A name designates whatever the person writing it meant, and common names are routinely generic where the substance is specific.
This is easy to state and was easy to get wrong, because the mistake does not look like trusting a name — it looks like trusting Common Chemistry, which is the most authoritative CAS source there is.
How a source list uses the two together
EF 3.1 pairs a readable common name with the registry number that says which substance is actually meant:
| EF 3.1 name | EF 3.1 CAS | What that number designates |
|---|---|---|
butanol |
71-36-3 |
1-Butanol |
ascorbic acid |
50-81-7 |
L-ascorbic acid |
carbon |
7782-42-5 |
Graphite |
iron oxide |
1345-25-1 |
Ferrous oxide |
pyrethrin |
8003-34-7 |
Pyrethrins |
None of these is an error. The name is the label a practitioner reads; the CAS is the identity. Carrying a generic name beside a specific number is exactly how a flow list stays readable without becoming ambiguous.
The failure
commonchem_cas_review looked up the flow's name in Common Chemistry and, on an
exact match, replaced the flow's CAS with the one Common Chemistry returned. For
the flows above, looking up the generic name returns Common Chemistry's
generic registry entry:
| Flow | Name lookup returned | Effect |
|---|---|---|
butanol |
35296-72-1 — unspecified butanol |
71-36-3 discarded |
ascorbic acid |
62624-30-0 |
50-81-7 discarded |
carbon |
7440-44-0 — carbon |
7782-42-5 discarded |
pyrethrin |
88108-26-3 — Pyrethrin |
8003-34-7 discarded |
Every one of those trades a specific number for a vaguer one, and discards the only field that said which substance was meant. In the August 2026 build this happened to 52 flow objects across 51 distinct registry numbers, and 51 of the 51 numbers discarded were valid registered CAS numbers that Common Chemistry itself knows.
The claim being made was never checked against the claim being used. "Common
Chemistry says the name butanol maps to 35296-72-1" is true. "A flow named
butanol carrying 71-36-3 is wrong about 71-36-3" does not follow from it.
The cost showed up downstream. EF's pyrethrins flow lost 8003-34-7, which is
the number ecoinvent's six Pyrethrins rows ship — so those rows could no
longer reach the substance they belong to by any identifier, and had candidates
only through an EC number shared with something unrelated.
The rule
An exact name match fills in a CAS the flow does not have. It never replaces
one the flow does have. Where the two disagree, the flow's number stands and
the disagreement goes to the commonchem-name-cas queue with applied: false.
This is the same rule the mirror case already followed. When Common Chemistry knows a flow's CAS and calls it something else, nothing is changed and a report is filed — because "a name is not evidence of identity the way a CAS is". That is the same disagreement approached from the other side, and it cannot have two answers depending on which lookup happened to find it.
What this gives up
The old behaviour caught two genuine errors, both in the same build:
| Flow | CAS it carried | What that number actually is |
|---|---|---|
1,1,1-trichloroethane |
79-00-5 |
1,1,2-Trichloroethane — the wrong isomer |
1,3,5-triazine |
121-82-4 |
RDX — an unrelated explosive |
These are real findings and they are still reported; they are no longer applied automatically. That is the right trade. A rule that corrects two flows and damages forty-nine is not a correction rule, and neither error is safe to fix without someone looking — the flow may be mislabelled rather than mis-numbered, and only a curator can say which.
Implemented in transformers/commonchem_cas_review.py; see
the review app for the queue.
The guards that stop bad merges
Each of these exists because a specific, reproducible failure was found in real data. They are worth understanding because they define what "matched" means in this project.
The CAS cross-check: don't trust a second-hand cross-reference
ChEBI IDs for a flow are resolved two ways. ChEBI-direct uses ChEBI's own CAS cross-references — the ChEBI record itself claims the CAS. PubChem-derived is a second hop: PubChem says "this compound has CAS Y and is also CHEBI:Z".
The second hop is unreliable. CAS 25013-16-5 (butylated hydroxyanisole, C₁₁H₁₆O₂) was mapped by PubChem to CHEBI:17688, which is (S)-nicotine, C₁₀H₁₄N₂. The two share no structural relationship, and the ChEBI record for nicotine does not list that CAS. Unguarded, all 105 of the nicotine record's cross-references — Beilstein IDs, PubMed articles, DrugBank, HMDB — would have been attached to butylated hydroxyanisole.
The rule: a PubChem-derived ChEBI ID is kept only if the ChEBI record's own CAS numbers overlap the CAS numbers being queried. No overlap, no match.
This guard also fixes a subtler problem. When related stereoisomers share a CAS
family, PubChem may map all of them to one ChEBI entry — and that entry may
carry stereochemistry-specific synonyms like (+)-(1S,3S,4R)-menthol. Without
the cross-check, that name spreads to (−)-menthol, D-menthol, and racemic
menthol, all of which are different substances.
The formula check: ChEBI is not infallible either
ChEBI-direct matches skip the cross-check, because ChEBI is asserting the CAS itself. But ChEBI records occasionally claim a CAS that belongs to a different substance. CHEBI:30146 (lithium hydride, HLi) lists CAS 7439-93-2, which is the CAS for lithium metal (CHEBI:30145, Li). Unguarded, lithium hydride's synonyms ("hydridolithium", "[LiH]", "hydrure de lithium") become synonyms of the lithium element, and its formula HLi contaminates the element's properties.
The rule: when several ChEBI-direct records match one CAS and the flow has a known formula, candidates are scored for formula similarity and those below a similarity threshold are discarded. Li vs HLi scores about 0.50, below the 0.55 threshold, so lithium hydride is rejected for the lithium element. Identical formulae (stereoisomers) score 1.0 and are always kept. A record with no formula is kept — absence of data is not evidence against a match.
The flow's own formula comes from its molecular_formula property, and from
what its name implies. That is the whole basis: there is no separate
flow-level formula field, which is why the guard is silent for flows whose
structure has not yet been enriched.
The primary-CAS rule: pick the compound that owns the number
A PubChem compound lists a primary CAS — the first in its own identifiers — plus secondary cross-references. When one CAS maps to several compounds, the one whose primary CAS matches is the right one.
CAS 7440-38-2 is elemental arsenic; CAS 7784-42-1 is arsine (arsane). PubChem lists both numbers on both compounds:
| PubChem CID | Substance | Formula | Primary CAS |
|---|---|---|---|
| 23969 | arsane | AsH₃ | 7784-42-1 |
| 5359596 | arsenic | As | 7440-38-2 |
ChEBI has no CAS entry for either number, so the ChEBI cross-reference strategy fails and selection falls through to "first compound in PubChem's listing order" — which for 7440-38-2 is arsane. That produced a three-stage cascade: the arsenic flow was renamed "arsane"; then, because names from all compounds sharing a CAS were pooled into one candidate list, "arsenic" was available as a name for arsine and the readability scorer preferred it over "hydrogen arsenide", so arsine was renamed "arsenic"; then element enrichment matched elemental arsenic to the flow now labelled "arsenic" — the arsine flow — and enriched it with the wrong element's properties.
The rules: prefer the compound whose primary CAS equals the queried CAS; and propagate a compound's names only to its primary CAS, not to its secondary cross-references.
The curated-CAS gate: a vendor catalogue is not a registry
The primary-CAS rule above decides between compounds during consensus matching. The same question arises earlier, where structures and names are written onto a substance, and there it used to go unasked.
CAS numbers are looked up through PubChem's xref/RN cross-reference index.
That index aggregates the CAS numbers of every substance record standardised
onto a compound — and a substance record can be a chemical vendor's catalogue
listing. A vendor that types the wrong number into its catalogue puts that
number on a compound it does not belong to.
CAS 686-31-7 is tert-amyl peroxy-2-ethylhexanoate. xref/RN returns three
compounds for it:
| PubChem CID | Formula | Substance |
|---|---|---|
| 102465 | C₁₃H₂₆O₃ | tert-amyl peroxy-2-ethylhexanoate |
| 121489259 | C₁₃H₂₆O₃ | the same, (2R) enantiomer |
| 106206 | C₁₈H₁₂MgN₂O₂ | magnesium bis(quinolin-8-olate) |
The magnesium salt is there because two vendor listings carry 686-31-7 on it. The number appears nowhere on that compound's own page — not in its synonyms, not in its CAS section. It exists only in the cross-reference index.
Merged unchecked, the substance published two molecular formulas, three InChI,
four SMILES and ten IUPAC names spanning two unrelated compounds. It was also
typed ChemicalSalt, because semantic typing reads the first SMILES of a sorted
list and the magnesium salt sorts first.
PubChem curates a CAS section on each compound, admitting only numbers a registry or regulator attests, with a reference per source. For CID 102465 it gives 686-31-7 from ten of them — AICIS, CAMEO Chemicals, CAS Common Chemistry, ChemIDplus, EPA CDR, EPA TSCA, EPA DSSTox, ECHA, FDA GSRS, NZ EPA. For CID 106206 it gives 67952-28-7 and 14639-28-2, and not 686-31-7.
The rule: a compound is dropped from a CAS lookup when its own curated CAS section does not list the number searched on. Two conditions keep this from overreaching:
- Something better has to exist first. A compound is dropped only when another candidate for the same CAS is attested. Where no candidate is attested, all are kept and the substance stays as ambiguous as it was — the pipeline does not guess.
- Silence is not denial. Where no curated record has been fetched for a compound, it is kept. Only a record that was retrieved and does not list the number counts as evidence against.
That second condition is why the identifier cache stores an empty record for a compound PubChem holds no registry identifiers for. "Asked, and there are none" and "never asked" are different answers, and only the first may drop a candidate.
Every CAS the gate narrowed is listed in the CAS links PubChem does not curate queue of the review application, with the compounds kept, dropped, and not asked about.
A semantic type can change without the substance having changed. Type assignment reads the first SMILES of a sorted list, so removing a candidate can change which structure it reads. Over a 400-flow verification run the gate narrowed 55 substances to a single structure, took the count carrying more than one from 135 to 80 — and moved 15 semantic types, 10 of them on substances that are still ambiguous and whose type was therefore already arbitrary. That arbitrariness is a known limitation, not something this rule introduced; the gate makes it visible by changing which arbitrary answer comes out.
The structure gate: a registry number need not denote a structure
The curated-CAS gate above decides which compound a CAS number means. It cannot help when a CAS number does not mean a compound at all.
Plenty of registry numbers name a UVCB, an unspecified isomer, or a commercial
mixture. 1300-21-6 is "dichloroethane" with no isomer stated; 68475-60-5 is
"alkanes, C4-5"; 8006-64-2 is gum turpentine. None of them denotes one
molecule. PubChem answers all of them anyway, because its cross-reference index
returns whatever compounds were standardised onto the number:
| CAS | Substance | PubChem returns |
|---|---|---|
| 1300-21-6 | dichloroethane, isomer unstated | CID 6365, 1,1-dichloroethane |
| 68475-60-5 | alkanes, C4-5 | CID 8003, n-pentane |
| 8006-64-2 | gum turpentine | CID 6506, triethyl citrate |
| 111937-03-2 | isononanoic acid, C16-18 esters | CID 962, water |
Each returns a single compound, which is why neither the curated-CAS gate nor the formula-similarity filter caught them: both treat one candidate as unambiguous, and an unopposed wrong answer is not ambiguous. The mixture then carried a specific molecule's structure, and collided under InChIKey with the substance it had borrowed it from — the 1300-21-6 flow object against the real 1,1-dichloroethane at 75-34-3.
CAS Common Chemistry is the source that can tell these apart, because it is the registry — but only partly, and the part it cannot is the reason this rule has a curated component. Its records come in four shapes:
75-34-3 1,1-Dichloroethane C2H4Cl2 InChIKey=SCYULBFZEHDVBN-…
68475-60-5 Alkanes, C4-5 Unspecified (no key)
1300-21-6 Dichloroethane C2H4Cl2 (no key)
10028-15-6 Ozone O3 (no key)
The second is unambiguous: CAS is saying the number has no definite composition at all. The third and fourth are identical in the record and opposite in meaning — an isomer family, and a single molecule Common Chemistry simply holds no key for. 319 of the 836 answered-with-no-key records are that shape, and they include ozone, nitric oxide, nitrogen dioxide, chlorine dioxide and doramectin. Reading them all as non-specific would strip ozone's structure.
The rule, in three parts:
- A number with no composition lends no structure.
molecularFormula: "Unspecified"is CAS stating there is nothing definite to give, so neither PubChem nor ChEBI is consulted through that number, however many or few candidates it has. 517 cached records. The number itself is still published and still links to its Common Chemistry and ECHA pages — only the structure is withheld. - A number with a formula and no key changes nothing, unless a curator has
ruled on it. The default is to do nothing, so a substance is only ever
denied a structure by an explicit entry in
commonchemistry-structure-decisions.json, each carrying a mandatorycommentsaying why the number is ambiguous. An entry without one is ignored. - Where Common Chemistry does give a structure, the candidate matching it wins. One-sided in the same way as the curated-CAS gate: a contradicted compound is dropped only when another candidate is confirmed. Common Chemistry is strong evidence and not an oracle — for metaldehyde, CAS 9002-91-9, it publishes acetaldehyde's InChIKey where our tetramer is the better answer — so a contradiction with nothing better to replace it goes to the queue rather than deleting a structure.
An empty cache record is neither answer. It means a 404 or a request that failed after its retries, and 286 of the 7,409 cached entries are that shape. Only a named record counts; an unnamed one changes nothing. A gap in the cache must never withhold a structure, which is the same rule the curated-CAS gate follows for compounds it never asked about.
Comparisons ignore the standard/non-standard flag — the 14th character of an
InChIKey's second block — when the keys agree. Common Chemistry publishes
non-standard keys freely and every other source here publishes standard ones, so
…-CDRYSYESSA-N and …-CDRYSYESNA-N are the same molecule. 22 flow objects
agree with Common Chemistry only once that character is disregarded.
When the keys disagree, that flag decides whether they disagreed at all. A
non-standard InChI is computed under options the key does not record, and CAS
reaches for them exactly when standard InChI cannot say what it needs — most
often /s2, relative stereochemistry, which standard InChI has no layer for:
Trans-4-tert-butylcyclohexanol CAS CCOQPGVQAWPUPE-KYZUINATNA-N
InChI=1/C10H20O/…/t8-,9-
(2RS,4SR)-2-methyl-4-propyl- CAS GKGOLPMYJJXRGD-HGXVMFPFNA-N
1,3-oxathiane InChI=1/C8H16OS/…/t7-,8+/s2
40 of the 75 non-standard keys in the cache carry /s2. No standard key can
ever equal one of them, so a difference against one is not the registry
contradicting a structure — it is two hashes of different things. Those get a
third verdict, incomparable, and the candidate is kept exactly as an
uncached CAS's candidates are: nothing was said against it. A skeleton
difference is still a contradiction whatever the flags say, because no InChI
option moves the connectivity hash.
Against the 2026-08-07 build, with 33 seeded rulings, the gate withholds a structure from 181 flow objects carrying 2,286 elementary flows, and dissolves 49 of the 105 InChIKey collisions described in the identity limitations. Every CAS it acts on is listed in the CAS publishes no structure queue of the review application.
The stereochemistry gate: a flat registration is a statement
The gate above asks which substance a number means. This one asks which isomer of it, and it is the one rule here that is not comparative.
CAS registers 4170-30-3 as Crotonaldehyde with a flat InChIKey —
MLUCVPSAIODCQM-UHFFFAOYSA-N, no stereo layer — and registers the trans
isomer separately as 123-73-9. The flat key is not a gap in the record. It is
the registry saying which of the two substances the number denotes:
| CAS | Substance | Key |
|---|---|---|
| 542-75-6 | 1,3-Dichloropropene | UOORRWUZONOOLO-UHFFFAOYSA-N |
| 10061-01-5 | cis-1,3-dichloropropene | UOORRWUZONOOLO-YFHOEESVSA-N |
| 4170-30-3 | Crotonaldehyde | MLUCVPSAIODCQM-UHFFFAOYSA-N |
| 123-73-9 | trans-crotonaldehyde | MLUCVPSAIODCQM-BUHFOSPRSA-N |
Every source a structure is looked up in disagrees, and all in the same
direction. ChEBI's entry for a common name is usually the stereo-defined natural
isomer; PubChem's CAS index answers with a compound it has a record for — and a
substance whose stereochemistry is unstated is not a compound either of them
holds. The flow object got the trans key, and then collided under InChIKey
with trans-2-butenal, a different substance with its own number.
None of the gates above can catch that, and it is not an oversight in them. They weigh candidates against each other and act only when one is better. Here there is no better candidate: in 20 of the 33 affected flow objects PubChem and ChEBI hand back the same stereo-specific structure, so nothing is contradicted and nothing is confirmed, and the narrowing gate returns early exactly as designed. Waiting for a source to disagree is waiting forever.
The rule: where Common Chemistry publishes a flat key for a number, from a standard InChI, a candidate structure that shares its skeleton and carries stereochemistry is refused — from PubChem and from ChEBI alike, however many sources offer it, and with no better candidate required. All three conditions are needed. A flat candidate agrees and is untouched, which is the overwhelming majority: 5,726 of the 6,300 keyed numbers in the cache are flat. A different skeleton is a different substance and a different problem, left to the gates above. And a flat non-standard key states nothing, because those options can suppress stereochemistry the substance really has.
Because the refusal can empty the candidate list, the structure CAS does
publish is put back in its place — the key, the InChI and the formula, under
enrich_references.commonchemistry_semantic. This is the only place Common
Chemistry is a source of structure rather than a referee of other sources'
structures, and it earns that here because it is the only source that holds the
answer. Withholding alone would trade one defect for another: a flow object with
no chemical identity at all. No SMILES is invented, because a value computed
from the InChI belongs to the stage that computes it.
On the 2026-08-12 build the gate acts on 90 registry numbers, withholding 27 PubChem compounds and 70 ChEBI records across 100 flow objects. The "stereo invented" band of #281 goes from 33 flow objects to nought, and all 33 now publish the key CAS registers and no other. Its "lost" and "conflict" bands are untouched, as is every one of the skeleton disagreements — this rule reaches only the isomer question.
No flow object lost its structure: all 100 still publish an InChIKey, an InChI
and a SMILES. The collisions the invented stereochemistry manufactured are gone
— Crotonaldehyde now holds MLUCVPSAIODCQM-UHFFFAOYSA-N against
Trans-2-butenal's -NSCUHMNNSA-N, and 1,3-dichloropropene
UOORRWUZONOOLO-UHFFFAOYSA-N against the (Z)-isomer's -UPHRSURJSA-N.
Every number it acts on goes to the review queue: the registry's flat key is the whole of the evidence, and where the isomers are not separately registered a curator should be able to call it a gap rather than a statement. Three of the 100 are flows whose name asserts a stereochemistry — a racemate, a reaction mass, and one whose own source InChI is flat and so agrees with the registry against its name. All three are in the queue, which is where that judgement belongs.
Reading the structure, not the fingerprint
The gate above answers one question — does this number have a stereochemistry at all — and it answers it from CAS's InChIKey. That is a fingerprint of a structure, not the structure, and a fingerprint records how it was taken. CAS takes many of its fingerprints a different way from every other source here, and two taken differently cannot be held up against each other. For those numbers the honest answer used to be "no comparable answer", and a real defect and a false alarm looked exactly alike.
But CAS also publishes the structure itself, alongside the fingerprint. A structure can be read and written out again — and then a fingerprint taken our way, which can be compared:
trans-4-tert-butylcyclohexanol, 21862-63-5
CAS publishes CCOQPGVQAWPUPE-KYZUINATNA-N
read and retaken CCOQPGVQAWPUPE-KYZUINATSA-N
^^^^^^^^ the same stereochemistry, all along
On the 2026-08-12 build that answers 22 of the 56 substance-and-number pairs where CAS states a stereochemistry the list does not match — and for a loss it does more than answer, because the corrected value is exactly what was just computed. Seven of the ten losses now arrive at the review queue with the key the substance should carry, rather than only the news that it is wrong.
The other 34 must stay unanswered, and that is the part to be careful about. CAS reaches for its own way of taking a fingerprint mostly to say something standard chemistry notation cannot: a relative stereochemistry — "these two centres are arranged so with respect to each other, in either mirror image". RDKit does not refuse such a structure. It reads it and hands back one of the two mirror images, with nothing in the answer saying a choice was made:
(2RS,4SR)-2-methyl-4-propyl-1,3-oxathiane, 59323-76-1
CAS says GKGOLPMYJJXRGD-HGXVMFPFNA-N either mirror image
RDKit returns GKGOLPMYJJXRGD-JGVFFNPUSA-N one specific mirror image
Accepting that would invent a stereochemistry the registry never claimed, which is the defect this whole section exists to stop. So the relative and racemic cases are detected before any conversion is attempted, and return nothing.
Two further notes for anyone extending this. Enhanced stereochemistry — RDKit's model for "either mirror image" — holds the meaning correctly in memory but produces the same fingerprint as the absolute form, so it is no way round the problem. And a fingerprint showing no stereochemistry has two causes it writes identically: a molecule with nothing to state, and a molecule whose arrangement was not stated. Counting stereocentres tells them apart, and anything deciding whether a substance has lost its stereochemistry needs that difference, or it reports every simple molecule in the list.
How a stereochemistry difference is decided
flowchart TD
A["CAS publishes a structure for this number"]
A -->|"no"| Z["Nothing to say"]
A --> P{"Same skeleton, and the same number of hydrogen ions?"}
P -->|"a different skeleton"| X["A different substance — #261 and #264, not a question about shape"]
P -->|"same skeleton, different charge"| Q["<b>protonation</b> — an acid and its ion. Not a question about shape either"]
P -->|"both the same"| Y{"Does its key already agree with ours?"}
Y -->|"yes"| Z2["Nothing to say. Equal stereo hashes are the same structure whatever computed them, so no conversion can change this"]
Y -->|"no"| B["Read the structure CAS published"]
B --> C{"Can it be re-expressed as a standard InChI?"}
C -->|"no"| D["No comparable answer exists. Report as <b>incomparable</b>, <i>saying which of the three reasons applied</i>, and ask a curator"]
C -->|"yes"| E["Take a fingerprint our way, so the two can be compared"]
E --> F{"How does it compare with what the substance publishes?"}
F -->|"the same"| Z
F -->|"substance has none, CAS has one"| G["<b>lost</b> — the corrected key is the one just computed, and is published"]
F -->|"substance has one, CAS has none"| H["<b>invented</b> — refuse the stereoisomer and publish what CAS registers"]
F -->|"both have one, and they differ"| M{"Is one record the other with some arrangements left undetermined?"}
M -->|"the registry's is"| N["<b>cas-undetermined</b> — a gap in the registry's drawing. Our record stands"]
M -->|"ours is"| O["<b>ours-undetermined</b> — our key names a broader substance than the number does. Fill it from the registry"]
M -->|"neither"| I["<b>conflict</b> — a real disagreement on a valid comparison. Ask a curator"]
G --> J{"Does the substance publish a stereochemistry-bearing key for this skeleton elsewhere?"}
J -->|"yes"| K["Not a loss — the flat key is the deliberate simplification"]
J -->|"no"| L["A genuine loss"]
Agreement is asked first, and it costs nothing. Two keys with the same stereo hash describe the same structure whatever computed them — that is the half of
281's warning about the standard flag that was always right, and it means a
conversion cannot change the answer where the keys already agree. Asking it
second is how Zinc-65 came to be reported as an impossible comparison against a
key identical to its own
(#349).
A refusal names its own reason. Re-expressing CAS's structure declines for three different reasons, and reporting the wrong one is a defect in its own right:
| Reason | What it means | Where it comes from |
|---|---|---|
relative |
A relative or racemic arrangement, which standard InChI cannot write down. Converting would pick one mirror image and not say so | 40 of the 75 non-standard keys in the cache |
altered |
The structure did not survive the round trip, so it cannot be trusted. RDKit's InChI reader drops the isotopic layer of a lone labelled atom — InChI=1S/Zn/i1+0 comes back as InChI=1S/Zn |
every isotope-labelled single atom |
unreadable |
Empty, or RDKit would not parse it | — |
The middle one is the guard working, not a stereochemistry. It used to be reported as the first, which is how a queue row came to describe a single zinc atom as having a relative stereochemistry.
The branch marked invented is the gate described above. conflict is reported and left alone: both sides state a shape, the comparison was valid, and nothing here can say which is right. The final question — whether a flat key sits beside a specific one — is what separates a real loss from the simplification the list publishes on purpose, and is #334.
The two questions at the top are asked first on purpose, and the charge one is #348: see an acid and its ion below.
A gap is not a contradiction
"Both sides state a shape and the shapes differ" turned out to be three findings wearing one label, and the split matters because two of the three are not disagreements at all (#350).
α-cyclodextrin is a ring of six glucose units, and each unit has five corners whose arrangement matters, so the molecule has thirty of them. Our record describes all thirty; the registry's describes twenty-nine and leaves one blank:
ours …,22-,23-,24-,25-,… corner 24: pointing this way
CAS …,22-,23-,24?,25-,… corner 24: not determined
Every corner both records describe, they describe identically. But a fingerprint is computed over the whole set of corners at once, so one blank in thirty produces a completely different result — as different as inverting all thirty would give:
ours HFHDHCJBZVLPGP-RWMJIURBSA-N
CAS HFHDHCJBZVLPGP-FXNRASGISA-N
A fingerprint does not degrade gracefully, so nothing comparing these two records by fingerprint can tell "the same substance, described once fully and once with a gap" from "two different substances". Fifteen rows arrived in the review queue labelled identically for that reason.
The question is answered by construction rather than by reading the layers off. Take the fuller record, mark as undetermined exactly what the sparser one leaves undetermined, and ask InChI for the fingerprint of what remains. If that is the sparser record's fingerprint, the sparser record is the fuller one with those corners unstated, and the two agree everywhere both speak.
Comparing the arrangement layers directly is the obvious approach and it is wrong, in a way that matters for two of the fifteen:
chloralose ours /t2?,3-,4+,5+,6?,7+/m0/s1
CAS /t2-,3+,4-,5-,6-,7-/m1/s1
Every assigned corner differs, and so does the flag saying which mirror image the record was written for. That reads as a flat contradiction and is not one: blank corners 2 and 6 in the registry's record and the rest re-canonicalises to exactly ours. The mirror flag records which of two mirror images the corner list describes, and which one that is depends on the set of corners being listed — so the two layers cannot be compared element-wise across records that describe different sets. Taking the fingerprint again is what avoids the trap.
The three answers want three different things:
| Kind | What it means | What happens |
|---|---|---|
cas-undetermined |
The registry leaves blank what we describe, and agrees everywhere else | Nothing. Our fuller record stands — a record that assigns every corner is not contradicted by one that leaves some undetermined. Reported at INFO so the next reader does not rediscover it |
ours-undetermined |
We leave blank what the registry describes | The same repair as a lost shape, below. Where it cannot be applied, the row stays open with the fuller key named in it |
conflict |
Each record states an arrangement the other contradicts | Reported at BLOCKING. Nothing here can say which side is right |
On the 2026-08-12 build the twenty conflict rows become seven
cas-undetermined, one ours-undetermined, five repairs and eight conflicts,
across 13 flow objects and 168 elementary flows. Where a structure cannot be
read at all the answer stays conflict, because that is the status quo and the
answer that sends a person to look.
Restoring a stereochemistry no source holds
A genuine lost is repaired from the registry's structure. It is the only
place in the pipeline where evidence is used to add a structural claim rather
than to refuse one, so it is a step of its own, under its own provenance name —
enrich_references.commonchemistry_stereochemistry — rather than part of a gate's
write.
The reason it is safe here and nowhere else is that the registry is the only
source that can answer. 21862-63-5 is trans-4-tert-butylcyclohexanol; the
trans form is not a compound PubChem or ChEBI holds a record for, so both answer
the number with the shape-free structure, and the flow object publishes
CCOQPGVQAWPUPE-UHFFFAOYSA-N — which is also plain 4-tert-butylcyclohexanol's
key, so the two substances collide on it. Waiting for a source to disagree is
waiting forever, exactly as in the invented direction.
Five conditions, and each refuses a case that would otherwise be wrong:
| Condition | What it stops |
|---|---|
| The registry's structure re-expresses as a standard key | A relative or racemic registration, where reading the structure back invents an absolute arrangement |
| The registry's structure states a shape at all, asked of the InChI and not the key | Zinc-65, whose key looks stereo-specific because the second block hashes isotopes too — the shape being copied would be an isotope label (#349) |
| Every candidate on the skeleton has a gap the registry fills — shape-free, or shape-incomplete in the sense above | Overwriting an answer a source did supply; a redundant flat key beside it is #334's business |
| Same skeleton | A different substance, which is #261 and #264 |
| Same protonation | An acid and its ion, which is #348 |
The partial case — a record missing part of its shape rather than all of it —
is the same repair, and it is the safer of the two, which is worth saying
because it looks like the bolder one. Filling a wholly shape-free record rests on
the registry alone: there is nothing in the candidate to agree or disagree with.
Filling a partial one rests on the registry having been checked against the
candidate first and found to state the same arrangement at every corner the
candidate describes. A record that contradicts ours at one corner and completes
it at another fails that test and stays a conflict.
β-Endosulfan is why it is worth doing. Five corners, of which the published
record describes two; the registry describes all five and agrees on the two.
α- and β-endosulfan are distinct regulated pesticides, and what distinguishes
them is exactly the corners left blank — so the published fingerprint was not
merely different from the registry's, it named a broader substance than the
registry number does. On the 2026-08-12 build the partial repair fires on 5
flow objects across 65 elementary flows.
The key published is the converted one, not the registry's own. Six of the seven wholly-lost numbers this fires on are registered with a non-standard InChI whose key cannot be compared with anything else in the list, and the InChI is published alongside it from the same conversion so that the two agree with each other. No formula is written: stereochemistry does not change one.
Nothing is done silently. Each repair appears in the stereo-disagreement queue
as a restored row at INFO, saying which key was replaced and by what. Where
the replaced key carried part of a shape rather than none, its withdrawal is
folded into that same row rather than reported again as a discarded claim: one
event, one row.
When the registry breaks a tie between two sources
A flow object can also end up with two stereochemistry-bearing keys on the same
skeleton — one from ChEBI, one from PubChem, with nothing in the pipeline
reconciling the two lookup paths against each other. Pyrethrin I publishes both
ROVGZAWFACYCSP-VUMXUWRFSA-N and ROVGZAWFACYCSP-NEWSRXKRSA-N, and anything
using an InChIKey as an identity sees two substances where there is one.
Where one of those keys is the structure CAS registers for the number, the others are withdrawn. That is a weaker claim than it sounds, and deliberately so: nothing is being chosen on Common Chemistry's authority. One of the object's own keys has been independently reproduced by the registry, which means the others describe something the registry does not call by this number.
The restraint is what makes it safe:
- A group where no key matches the registry is left entirely alone. That is
the
conflictband and the two undetermined bands above, which are settled or repaired rather than tie-broken — #350. - A group where the registry has no comparable answer — a relative or racemic registration — is left alone for the same reason.
- A flat key in the group survives. It states nothing the specific key contradicts, and it is #334's.
- Skeleton and protonation must match, so a different substance or a different ionisation state is never in the group at all.
On the 2026-08-12 build this withdraws a key from 8 flow objects across 106
elementary flows. Six are the pyrethrin esters — Pyrethrin I and II,
Cinerin II, Jasmolin I and II — where the withdrawn key is the surviving
one with the double-bond geometry left out. Because a structure claim is being
discarded rather than added, each withdrawal is reported at REVIEW, naming both
the key that went and the one that stayed.
One of the eight runs the other way, and the split above now says so out loud.
Oxymetholone's withdrawn key states a double-bond geometry the registry does
not state at all, so the same number carries a cas-undetermined row beside its
superseded one: our record described more, and the tie-break took it. Keeping
it instead would mean asserting a geometry the registry does not, on one source's
word, and leaving two identities on the object — which is the defect the
tie-break exists to remove. So the rule stays uniform and the row stays REVIEW,
which is what a curator is being asked to look at.
An acid and its ion are two substances, not two shapes
An InChIKey's last block records how many hydrogen ions the substance is
carrying. N is the neutral form; M, L, K are one, two and three ions
removed; O, P, Q are one, two and three added. It is the only block that
moves when an acid gives away a hydrogen ion, and the shape hash in the middle
does not move at all:
(2R,3R)-2,3-dihydroxybutanedioic acid, 87-69-4
published FEWJPZIEWOKRBE-JCYAYHJZSA-N tartaric acid, C4H6O6
published FEWJPZIEWOKRBE-JCYAYHJZSA-L tartrate, C4H4O6, two ions gone
CAS says FEWJPZIEWOKRBE-JCYAYHJZSA-N the acid, as (+)-Tartaric acid
^^^^^^^^ the same shape in all three
Both of those keys were on one flow object, one from ChEBI and one from
PubChem, in the field that says what the substance is. And because the
comparison against the registry reported a difference — correctly; a charge
difference is a real difference — the shape classifier read that answer as being
about shape, and filed the object at blocking as "same skeleton, different
stereochemistry, both specific" over two keys whose shape hashes are identical
character for character. A curator sent to settle a shape question would have
found there was no shape question to settle.
Charge is therefore tested before shape. The order is what fixes it: the answer to "is this a shape disagreement?" was already decided by something that is not a shape. Testing charge first also gets the question right when both differ, because the last block is not touched by the InChI options the shape hash depends on — so the charge comparison is valid on a pair where the shape comparison could only shrug, and the row reports what can be reported.
Where the charge differs, the two keys are two substances in one identity field, and the tie is broken exactly where the previous section breaks it: only where one of the object's own keys is the structure the registry publishes for the number. Nothing is chosen on Common Chemistry's authority; one key has been independently reproduced by the registry, so the other describes something the registry does not call by this number, and it is withdrawn.
The procedure, per registry number the object was reached through:
| Step | What it does |
|---|---|
| 1 | Re-express the registry's structure as a standard key, as everywhere else here. No comparable answer — a relative or racemic registration, or one that does not survive the round trip — and the object is left alone |
| 2 | Take the object's keys with the same skeleton, the same shape hash and the same InChI version. A different skeleton or a different shape is a different question, and has its own band |
| 3 | Is the registry's own charge among them? If not, stop: the registry has not reproduced anything, and choosing which ionisation state the object is for is a curator's judgement |
| 4 | Withdraw the rest, and report the removal at review, naming the key that went and the one that stayed |
Worked through on the example above: step 1 gives
FEWJPZIEWOKRBE-JCYAYHJZSA-N; step 2 keeps both published keys, since they
differ only in the last block; step 3 finds -N among them; step 4 withdraws
-L and leaves the object publishing tartaric acid alone, with a review row
saying tartrate was taken out and the acid stayed.
Step 2 is also why this needs no isotope guard, unlike the tie-break above: block 2 hashes isotopic labelling as well as shape, so radium-226 and radium-224 differ in it and never reach step 3 together. Two keys that do reach it carry the same label as well as the same arrangement (#349).
Unlike its stereochemistry sibling this asks nothing about shape, so a pair with
no shape at all is treated the same way — SLXKOJJOQWFEFD-UHFFFAOYSA-N and
-M, 6-aminohexanoic acid and its anion, are two substances for the same
reason. Those were reported nowhere before: the classifier handed a number
registered without stereochemistry straight to the shape gate, which has nothing
to say about charge.
Only the identity field is settled by this. The object above also publishes
two formulas, C4H6O6 and C4H4O6; two SMILES; two charges, 0 and -2; and
two masses. Those fields accumulate their candidates on purpose — a list there
states the ambiguity where a single value would hide it, which is
#127 — so
withdrawing the key does not touch them, and a reader joining on formula still
sees both substances.
On the 2026-08-12 build 15 flow objects publish two keys differing only in
charge. The registry's own answer is among the keys for 4 of them — the tartaric
acid above, Enoxolone, Nadide and Aminocaproic Acid — and those are the
withdrawals, across 52 elementary flows. The other 11 are mostly salts the
registry answers with a different skeleton altogether, so nothing is reproduced
and nothing is touched.
Two objects are reported as their own protonation rows at blocking, because
the registry does hold a key for the number and what the object publishes for it
is the ionised form: Cupferron (135-20-6), which is one of the 11, and the
ammonium salt of 34274-28-7, which is not — it publishes the ion and nothing
else, so it has no clash of its own. The remaining objects clash without the
registry saying anything either way. Finding those needs no registry lookup at
all, since two keys on one object differing only in the last character is always
this situation, and that check is what
#348 leaves
open.
The cross-object synonym strip: a name belongs to one substance
Two cleanup passes remove synonyms that create false equivalences:
- Any synonym matching the preferred name of a different flow object is removed. A substance cannot legitimately be called by another substance's preferred name. Flows of the same substance in different contexts share a preferred name and never trigger this.
- Element symbols (
Rn,[Rn]) belong exclusively to the pure element. When they appear on isotopes or compounds they are wrong. The pure element is identified by having exactly one molecular formula value equal to the bare symbol — isotopes carry extra values like["Rn", "[222Rn]"]and so are not mistaken for it.
| Flow object | Preferred name | Synonyms before | After |
|---|---|---|---|
fo-37b9… |
Radon | Rn, [Rn], niton, … |
unchanged — this is the element |
fo-3320… |
Radon-222 | (222)Rn, [Rn], Alphatron, … |
[Rn] removed |
Both run before consensus matching, so the scoring step sees cleaned label sets.
The catalogue strip: a product code is not a name
Common Chemistry and ChEBI file every string anyone has attached to a CAS
number, and nothing in either payload separates chemistry from commerce. Along
with octadecanoic acid, stearic acid arrives carrying F 1000, S 300,
Prifrac 2981, Radiacid 0152 and NSC 147337. 18.3% of published
alternative labels — 368,720 of 2,012,182 — are one of three shapes:
| Shape | Examples | Rows |
|---|---|---|
| Supplier grade | S 100, A 1, F 1000, P-30 |
243,048 |
| Trade name and product number | Garlon 480, Polytal 4641, Dow Corning 777 |
80,357 |
| Database accession | NSC 147337, AKOS000118800, C.I. 77120, UN 3077 |
45,315 |
These are not merely noise. A grade code is shared between unrelated products:
a merge once resolved Propylene Carbonate to Talc, because both carried a
K 3 alternative label and a label index cannot weigh what it cannot read.
Removing them costs little. The filter takes 368,720 labels and leaves 104 flows with none at all, out of 89,045 carrying any.
Why shape rather than agreement
The obvious alternative is the rule used everywhere else on this page: publish only what two sources agree on. It was measured against a full build and rejected.
| Rule | Alternative labels kept | Flows left with none |
|---|---|---|
| Publish everything a source supplies | 2,012,182 | 0 |
| Require Common Chemistry and ChEBI | 127,358 (6.3%) | — |
| Require any 2 of Common Chemistry / ChEBI / PubChem | 160,243 (8.0%) | 31,002 |
| Shape filter (what runs) | 1,643,462 (81.7%) | 104 |
Agreement fails here for a reason specific to names. Common Chemistry and ChEBI
agree on 5.8% of published strings — not because the other 94% are wrong,
but because one ships CAS-index inversions and trade names while the other
ships IUPAC and biochemical shorthand. The rule therefore removes oxidane,
trans-2,4-hexadienal, H(2)O and Common Chemistry's own primary name for a
substance alongside the grade codes, and would add 2,598 back.
Consensus measures whether two registries share a vocabulary. It does not measure whether a string is a name. For identity claims — which substance, which CAS — agreement is the right instrument and is used. For labels, shape is.
When the shape test is wrong
It is a heuristic, and there are two ways to overrule it.
A whole convention belongs in the patterns, in
transformers/strip_catalogue_altlabels.py. Two are already exempt:
- Refrigerant, halocarbon and halon designations —
HFC-134a,Halon 1211,Fluorocarbon 113,R-600a. EF 3.1 identifies 55 flows by designation alone, with no CAS, no formula and no structure (#202); deleting these would be a data loss.R-600awith a hyphen is isobutane as a refrigerant, soRis admitted only hyphenated —R 300with a space is a grade of stearic acid. - Congener numbering —
PCB 118,BDE 47. None appear in the current build; the exemption is pre-emptive.
A single string belongs in the keep list, altlabel-keep-list.json, which
is data rather than code:
{
"keep": [
{"value": "Some Label 400", "comment": "why this is a name and not a grade"}
]
}
Matching is case- and whitespace-insensitive, an entry applies to every flow
carrying that label, and comment is mandatory — an entry without one is
ignored and logged, because an unexplained exemption is one nobody can
re-evaluate later. The file ships empty: when it was written, no string the
filter removes was any flow object's preferred name, so nothing was known to
need overruling.
The strip step also names every value it removes in its change-log comment, so
/changes filtered to strip_catalogue_altlabels is where you find a label
worth keeping.
Some cases neither mechanism can fix, and the patterns are deliberately shy
about them. The trade-name test requires a three-digit product number, so
Tween 80 survives — it is a brand, but so is the form of Pigment Yellow
74 and Vitamin B12, and no shape test tells those apart.
When the evidence is not in the label
Silicon Dioxide published 4,003 synonyms after the shape filter had taken 38%
of what Common Chemistry supplied, and they were almost entirely commercial:
173 beginning Snowtex, 126 Aerosil, 75 Nipsil
(#242).
Snowtex 30 is shorter than the trade-name pattern requires and matches no
registry prefix, and no prefix list will ever hold it — brand tokens are
open-ended.
The signal is repetition on one object. 173 labels sharing a leading token is a supplier catalogue; a chemical vocabulary does not behave that way. Two formulations of the gate were measured against the build and failed:
| Gate | Why it fails |
|---|---|
| Leading token is a chemical word | Methyl Violet 10B, Pigment Blue 15:3 lead with chemical words and are product codes |
| Token appears on few other objects | Nissan spans 56 objects; R (103), S (74), A (73) are grade prefixes, not vocabulary |
What separates them is what the number is doing. In a name the digits are bound
into the chemistry — Sodium 2-mercaptopyridine 1-oxide. In a product code they
dangle — Snowtex 30. A hyphen between digits is a catalogue range
(Cataloid S 1-50), so only adjacency to lower-case chemistry counts.
The rule runs only above 100 alternative labels. The median object carries 9
and the mean 17, while the 96 above 100 hold 23.9% of every synonym in the
list — so the gate buys the concentrated part of the problem without exposing
Tween 80 to a rule it would fail. It removes 8,643 labels from 69 objects.
Where shape genuinely cannot decide, a register does. Ponceau 4R is a name
and Acetoquinone Blue R is a product, and the strings are the same shape;
ChEBI is consulted at run time and the public part of the Colour Index ships as
data. This is agreement used as an exemption — it may only keep a label,
never publish one — which is what makes it safe here after being rejected as
the primary filter above. It rescues 13 labels, Ponceau 4R, Tween 20,
Polysorbate 20, Laureth 4 and Laureth 9 among them.
The documented gap is Methyl Violet 10B. It appears in no open register —
not ChEBI, not Wikidata, and Colour Index International itself is
subscription-only — so it survives on the size gate alone, crystal violet
carrying 96 labels against a threshold of 100. The keep list is the mechanism
for a string in that position.
Structure is computed, not asserted
Names and registry numbers come from sources and may be wrong. Molecular formula, mass, and InChIKey can be derived from a structure, which is arithmetic rather than testimony.
Where a flow's IUPAC name parses unambiguously with OPSIN, its SMILES and InChI are replaced with values derived from that parse. RDKit then recomputes formula, masses, and InChIKey from the resulting InChI — but only for flows whose identity is unambiguous: exactly one IUPAC name, no origin qualifier, and an InChIKey unique across all non-qualified flows. When those conditions do not hold, the existing values are left alone.
This is why the eligibility conditions are strict. Computed values are only better than asserted ones when you are certain what you are computing from.
Salts: the formula and the structure do not say the same thing
Nickel(II) acetate is a nickel atom carrying a charge of two, with two acetate
ions beside it. That is C4H6NiO4.
Here is how CAS registers it, under 373-02-4:
name Nickel acetate
molecular formula C2H4O2 · 1/2 Ni
molecular mass 178.80
That reads as acetic acid, with half a nickel for each acetic acid — which is the same thing said the other way round. Two acetic acids to one nickel. It is the long-standing way chemical registries write a salt down: name the acid, because the acid is the part with a name, then say how much metal goes with it as a ratio. The mass confirms it: 178.80 is two acetic acids plus one nickel.
The composition is correct. The trouble starts when it is turned into a drawing.
Half an atom cannot be drawn
The two standard ways of writing a structure down — SMILES and InChI — describe one specific assembly of atoms. Neither has any way to write "half a nickel". So when the registry converts its own formula into a structure, the fraction has nowhere to go, and the ratio quietly becomes one-to-one:
the formula C2H4O2 · 1/2 Ni two acetic acids to one nickel
the SMILES [Ni].O=C(O)C one acetic acid to one nickel
the InChI InChI=1S/C2H4O2.Ni/c1-2(3)4;/h1H3,(H,3,4);
the InChIKey XMOKRCSXICGIDD-UHFFFAOYSA-N
That last key is not a mistake anybody made. It is the registry's own, computed
from the registry's own structure, and it describes the right substance with its
ratio rounded off. PubChem holds the same key, having taken it from the same
place, and publishes the rounded formula C2H4NiO2 — the 1/2 having been lost
one step earlier.
This is not one substance's quirk. 274 of the registry records this project has fetched carry a ratio that a structure cannot hold — 77 written as a fraction and 197 as a whole-number multiplier:
62-54-4 Calcium acetate C2H4O2 · 1/2 Ca
553-72-0 Zinc benzoate C7H6O2 · 1/2 Zn
14644-61-2 Zirconium sulfate H2O4S · 1/2 Zr
14807-96-6 Talc H2O3Si · 3/4 Mg
7758-16-9 Disodium pyrophosphate H4O7P2 · 2 Na
1113-38-8 Ammonium oxalate C2H2O4 · 2 H3N
Every one of those looks, to a program comparing structures, like the registry describing something other than the salt. It is describing the salt, in a notation that lost the ratio on the way out.
What this project does about it
Ask what the substance is made of, not what it looks like. The formula is the field where the ratio survives, so the formula is the field to compare.
This is the third way one object ends up publishing two identities, after the flat key beside a specific one and the acid beside its ion. The rule for a charge difference is An acid and its ion are two substances, not two shapes above, and it compares keys directly, because there the two keys share a skeleton. Here they do not: rounding the ratio off changes the skeleton as well as the charge, so that rule sees two unrelated structures and correctly declines. Comparing compositions is what reaches the cases it cannot.
Comparing has to allow for two things that are not disagreements:
- The ratio is multiplied out first.
C2H4O2 · 1/2 NibecomesC4H8NiO4before anything is compared, because half a nickel alongside one acetic acid is one nickel alongside two. - Hydrogen is ignored. A salt is its acid with the acidic hydrogens gone,
and the registry names the acid:
C4H8NiO4for the registry,C4H6NiO4for the salt. Ignore hydrogen and both are four carbons, four oxygens and one nickel — one substance, described from two ends.
Where a substance publishes two formulas and the registry backs one of them, the other is withdrawn, and so is every structure that came with it. That is step 19 of the chain.
The same test, pointing the other way
It would be easy to read the above as "the salt's own chemistry wins over the registry", and that reading is wrong. The test is about composition, and it cuts both ways.
1,4-diazabicyclooctane — DABCO — is a small cage of two nitrogens bridged
three ways, C6H12N2. Its name is properly written
1,4-diazabicyclo[2.2.2]octane; the [2.2.2] says how long the three bridges
are, and without it the name does not describe a molecule. The name-reading
software does not refuse it. It reads bicyclo as "two of", and returns a
completely different molecule, C14H28N2.
The registry says C6H12N2 for 280-57-9, and so do ChEBI and PubChem. So the
same comparison that keeps the name-derived answer for nickel acetate withdraws
it for DABCO. Nothing is decided on which part of the pipeline produced a
value — only on whether the registry's stated composition backs it.
What is deliberately left alone
- A substance publishing one formula is never touched, even where the registry disagrees with it. A substance with no formula at all is worse than one whose formula is disputed, and the dispute is already reported.
- Where the registry agrees with nothing the substance publishes, nothing is withdrawn. That disagreement is real and belongs to a curator. On the build of 17 August 2026 there were ten such substances, mostly borates and vanadates whose published formulas disagree among themselves as well.
- Ignoring hydrogen is allowed here and nowhere else. It makes ethane and ethene indistinguishable, which would be intolerable if this were deciding whether two substances are the same. It is not: it asks only whether a second value on one record contradicts what the registry registered.
When the evidence is not enough
The consensus matching step applies updates only under explicit confidence
rules — typically several independent sources agreeing. When they do not agree,
it does not guess. Instead the case goes into a queue in review_queue, and
/queue/<name> is where a human decides it:
| Situation | Queue |
|---|---|
| Common Chemistry disagrees with the flow's name for a CAS | commonchem-cas-name-differences |
| Common Chemistry gives a different CAS for the flow's name | commonchem-name-cas |
| A CAS resolves to several ChEBI records ambiguously | cas-ambiguous |
| An EC number fails its check digit and cannot be corrected | ec-malformed |
| CAS ↔ EC pairing contradicts the ECHA inventory | ec-cross-check |
| Consensus matching found no confident answer | consensus-match |
| A preferred-label rename is awaiting a ruling | undecided-label-replacement |
| Two live flows share a substance, a context and a unit, and a comment is all that separates them | elementary-flow-collision |
| A ChEBI record's formula diverges from the flow's | formula_mismatches table |
| An element has no flow object | element_coverage table |
| An ecoinvent flow matched no consensus flow object | merge_outcomes table |
Declining to act is a normal outcome, not a failure. See The review application for how to work through these queues.
Every value carries its provenance
Two independent records are kept:
- Field-level provenance — for a data value, which source asserted it,
recorded as a structured PROV record rather than free text. Carried on the
value itself, and traced across the run by
provenance_activities. - Which source stands behind which value. Where a field holds several
values, the provenance list above covers the field as a whole and cannot say
which source gave which value: DABCO's InChIKey field held two keys and three
provenance entries with nothing joining them. A companion
attested_byrecords, per value, which steps wrote or confirmed it. It is what lets the salt rule above withdraw the InChIKey that came from the same place as the formula it withdrew. A value nothing attests reads as unknown, never as unsupported, so an older record is never withdrawn for its silence. - The change log — for each field the pipeline wrote, which processing step
wrote it, the old value, the new value, and the reason. The
changelogtable, keyed by flow object id, withchangelog_flowsnaming the elementary flows each edit landed on. One row per edit: the biggest fields in the log belong to the substance, and an edit to a substance reaches every flow that shares it, so a row per flow said the same thing a dozen times over./changesshows all of it;/flows/<uuid>/changesshows one flow's, on demand.
Both are written by every run. They were optional JSON files until the review application was consolidated onto the database, which meant the record of why a value is what it is normally did not exist.
A value the pipeline declined to write is the same kind of fact. When two
flows are collapsed into one and both of them published a characterisation
factor for the same method, only one number can be published, and the other
used to disappear without trace — so nothing told a reader that the number they
were looking at had been chosen at all. The number not published is now kept on
the factor that superseded it, in superseded_values, with its own provenance
naming the flow that published it.
No processing step mutates a flow directly; each proposes changes that the engine applies and logs. That is what makes the change log complete rather than best-effort. Where several steps write the same field, the last one wins, and the log shows the sequence.