Skip to main content
Version: v0.0.2

Troubleshooting

Common symptoms when integrating @cosyte/terminology, and how to read what the engine is telling you.

translate returned unmapped - where's my code?

That is the engine working as designed. When a source concept has no target in the supplied map, the result is a typed unmapped - never a guessed code:

const result = translate(coding, map);
if (result.unmapped) {
result.code; // "TERM_TRANSLATE_UNMAPPED"
result.mode; // "fixed" | "provided" | "other-map" | "none"
result.source; // your original coding, surfaced untouched
}

If the map declares a group.unmapped fallback, its mode (and, for fixed, fixedTarget, or for other-map, otherMapUrl) is reported on the result - but never silently applied. You decide whether to trust a fallback.

resolveSystem returned { unknown: true }

The identifier (URI, OID, or mnemonic) is not one the engine recognizes. It returns a typed unknown rather than guessing a canonical URI. Check the value, and canonicalize known systems by their OID or HL7 v2 mnemonic (LN, SCT, I10C, …).

loadConceptMap threw

loadConceptMap throws a TerminologyError carrying TERM_CONCEPTMAP_MALFORMED when the resource is not a usable ConceptMap (wrong resourceType, a target missing its equivalence, and so on). Catch it and branch on err.code. This is deliberate: a structurally broken map is refused rather than loaded partially and translating some codes while silently dropping others.

Can I translate a target code back to its source?

Not through a forward map. Steward maps are directional and non-invertible, so translate only matches the map's source side. Reverse translation needs an explicit inverse ConceptMap.

validateCodeInValueSet returned undetermined - is my code a member or not?

The engine could not prove membership either way, so it refuses to guess. This happens when a part of the value set cannot be evaluated: an intensional filter/system include whose code system you did not pass in the ExpansionContext, a referenced value set that is not supplied, an unimplemented filter operator (regex / generalizes), or a truncated pre-computed expansion. The result carries code: "TERM_VALUESET_CANNOT_EXPAND" and a diagnostics list naming each gap.

const r = validateCodeInValueSet(coding, valueSet, { codeSystems });
if (r.undetermined) {
r.diagnostics; // each names the code system / value set it could not resolve
}

Supply the missing CodeSystem (or referenced ValueSet) in the context and re-check. A truncated server expansion must be re-fetched in full - the engine never treats a truncated snapshot as complete membership (a false "not a member" is a clinical error).

expand returned complete: false

The contains set is a lower bound, not exhaustive membership - some intensional part could not be computed (see above). Read the diagnostics for which code system or filter was missing; do not treat the partial contains as the whole value set.

Diagnostics and logs

Diagnostic and error message fields are value-free - they carry a stable code and, at most, a code + system + version, never a patient identifier. Still, remember that a code in patient context can be PHI: never log the surrounding record.

Known limitations (this release)

Status: @cosyte/terminology ships the code-system identity resolver, the ConceptMap $translate engine, the CodeSystem load layer with $lookup / $validate-code, the ValueSet compose / $expand / binding operations, UCUM validation, the crosswalk resolvers, and the RxNorm drug graph.

  • BYO data - $expand and binding operate over the CodeSystem releases and referenced ValueSets you supply in the ExpansionContext; an intensional part with no supplied code system is a typed TERM_VALUESET_CANNOT_EXPAND, never a fabricated member.
  • Subsumption is the release's own hierarchy - is-a / descendent-of read the loaded code system's parent edges (nested concepts or an explicit parent property). Subsumption across two separate releases is not computed.
  • Intensional filters are best-effort - is-a / descendent-of / is-not-a / = / in / not-in / exists are implemented; regex / generalizes surface as TERM_VALUESET_CANNOT_EXPAND.
  • RxNorm graph is BYO - loadRxNormGraph operates over the RxNorm RRF release you supply; the engine bundles no RxNorm content. NDC↔RXCUI resolution is release-scoped and carries the as-of release; obsolete/alien NDC statuses come from RxNav NDC-history data (a differential source), not the base RRF concept files, and are never fabricated. Approximate matching is opt-in and always labeled. An RXCUI whose supplied atoms could not establish a term type is left out of the graph and reported as TERM_RXNORM_UNTYPED_CONCEPT on graph.warnings, never loaded under a synonym's TTY; check that list if a concept you expected reads as TERM_RXNORM_UNKNOWN_RXCUI.
  • No bundled code-system release - every code-system release is bring-your-own, including the content packs (RxNorm Prescribable, ICD-10-CM) and SNOMED CT / CPT / LOINC / UMLS / VSAC.
  • What is bundled: the UCUM unit table (ucum-essence.xml v2.2, copyright © Regenstrief Institute, Inc., reproduced verbatim under https://ucum.org/license; notice at vendor/ucum/NOTICE.md, which ships with the package), the code-system identity pairings, and the SNOMED CT concepts the crosswalk resolver names - the map-category concepts and the two gender findings (SNOMED CT is copyright © International Health Terminology Standards Development Organisation). So the UCUM operations and resolveSystem work with no release at all; $lookup, $validate-code, $expand, $translate and the crosswalk and RxNorm resolvers need one you supply.

The API Reference always reflects exactly what this release ships - treat it as the source of truth over any prose above.