Skip to main content

fhir-code-lint

Your FHIR test data is probably lying to you. This catches it.

fhir-code-lint validates every coded value in a FHIR R4 bundle against its authoritative source and flags the ones that disagree: wrong codes, retired codes, and displays that don't match what the code actually means.

pip install fhir-code-lint
fhir-code-lint path/to/bundles/
=== ERROR (3) ===
  [patient_07.json] MedicationRequest rxnorm:1116634 display='Tiotropium 18 MCG Inhalation Capsule'
    -> display disagrees with canonical 'ticagrelor Oral Tablet'
  ...
summary: 3 error(s), 2 warning(s) across 8 file(s)

Why this exists

During a review of a healthcare RAG prototype, a physician panelist noticed a medication card displaying a COPD inhaler while citing education material for a blood thinner. The pipeline was correct. The RxNorm code in the synthetic patient record was wrong, and every downstream system that trusted it (retrieval, display, education links) was confidently wrong with it.

The validator I wrote in response caught two more wrong medications and several deprecated codes in the same small dataset: one case from Synthea output, two in bundles served by the public HAPI FHIR test server. If your pipeline joins coded clinical data to external knowledge, check code/display consistency at ingest.

The three that motivated it

The dataset behind that story was a handful of Synthea patients plus bundles pulled from the public HAPI FHIR test server. It contained these, each verified against RxNav:

The record said RxCUI it carried RxNav says that code is
Tiotropium 18 MCG Inhalation Capsule (a COPD inhaler) 1116634 ticagrelor Oral Tablet (a blood thinner)
Morphine 1mg IV 73032 remifentanil (a different opioid)
Oxybutynin Chloride 5 MG Oral Tablet (overactive bladder) 198369 torsemide 10 MG Oral Tablet (a diuretic)

Check any of them yourself:

curl https://rxnav.nlm.nih.gov/REST/rxcui/1116634/properties.json

A physician reviewer caught the first one by accident. The validator caught the other two on purpose, plus four retired codes, the same afternoon.

What it checks

System Source of truth Mismatch severity
RxNorm RxNav properties (name + synonym) error: RxNav is canonical
SNOMED CT MedlinePlus Connect topic titles warning: titles are patient-friendly synonyms
LOINC not yet skipped in v0.1

Also flagged: missing codes, junk displays (n/a, test, ...), RxCUIs that no longer resolve (wrong or retired), and medications coded in SNOMED rather than RxNorm, which silently break RxNorm-keyed pipelines.

Resource types covered: Condition, Observation, MedicationRequest, MedicationStatement, Procedure, AllergyIntolerance, Immunization, DiagnosticReport.

flowchart LR
    A[coded value] --> B{code present?}
    B -- no --> E[error]
    B -- yes --> C{junk display?}
    C -- yes --> E
    C -- no --> D{system}
    D -- RxNorm --> R[RxNav, canonical]
    D -- "SNOMED-coded medication" --> W[warning]
    D -- SNOMED --> M[MedlinePlus, synonyms]
    R -- unknown code --> E
    R -- tokens disjoint --> E
    M -- no topic --> W
    M -- tokens disjoint --> W

Matching, honestly described

Displays and canonical names are compared as sets of lower-cased alphabetic tokens with dose-form stopwords removed ("Metformin 500 MG Oral Tablet" reduces to {metformin}). Disjoint token sets are a finding. This deliberately favors false negatives over false positives: it will not flag "Cholesterol Levels" against "Hyperlipidemia" as an error (that mismatch is a warning from a non-canonical source), but it will catch tiotropium wearing a ticagrelor code, which is the failure mode that matters.

CI usage

fhir-code-lint data/fhir/ --format json > findings.json   # machine-readable
fhir-code-lint data/fhir/ --strict                        # warnings also fail

Exit codes: 0 clean, 1 findings at failing severity, 2 usage error. Lookups are cached per run and rate-limited (--delay, default 0.1s) to be polite to the public NLM APIs.

Library usage

from fhir_code_lint import validate_paths, collect_paths

findings = validate_paths(collect_paths(["data/fhir/"]))
for f in findings:
    print(f.severity, f.file, f.code, f.detail)

FAQ

Does it need credentials? No. RxNav and MedlinePlus Connect are public NLM services.

Does it validate against Synthea specifically? It validates any FHIR R4 bundle. The code that motivated it was in Synthea output and two more came from the public HAPI test server, which is why generated and shared test data should both be linted: statistical validity and coding validity are different properties.

Why is LOINC skipped? Deterministic LOINC validation needs the LOINC table license flow. Planned, with no date attached.

License

Apache 2.0.

Built by William Garrow.

Metadata

Release files for fhir-code-lint 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fhir-code-lint 0.1.0
File Size Uploaded Provenance
fhir_code_lint-0.1.0.tar.gz 13.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for fhir-code-lint 0.1.0
File Interpreter ABI Platform
fhir_code_lint-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.2 kB

Release files / fhir_code_lint-0.1.0.tar.gz

Download URL fhir_code_lint-0.1.0.tar.gz
Size 13.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b18cbbc0f7dcbf190aa818747ec3a66a50660d00746ad6cb4501344677010854
BLAKE2b-256 checksum
How to use checksums
c9093092ce62c9938b4ad93d31f918558de27007cc9532f031932c5737bc0169
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

Release files / fhir_code_lint-0.1.0-py3-none-any.whl

Download URL fhir_code_lint-0.1.0-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6186d4c2dbbba1dffd0468aff7b57be79620f84bb10d4baf4638447a67f2eab9
BLAKE2b-256 checksum
How to use checksums
5783ad01a4dc5e557d6d1d9b69cedfe8a32177511076529bcd6a6097ce9143f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

Security for fhir-code-lint 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for fhir_code_lint-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page