Troubleshooting
Common questions when generating fixtures with @cosyte/synth.
"I got a different message than last run"
Check the seed first. The same seed gives byte-identical output, and synth passes a seeded control
ID and timestamp to each builder so nothing depends on the clock:
import { generateAdt } from "@cosyte/synth/hl7";
generateAdt({ seed: 5 }).toString() === generateAdt({ seed: 5 }).toString(); // => true
If the seed is the same, a version moved. A synth release can change the bytes a seed maps to,
and when it does, its changelog entry says so under "Output change" and names each format. A parser
upgrade can change them with synth unchanged, because each format's bytes come from that parser's
builder. Pin the seed, the synth version, and the parser versions together;
Pin a golden fixture shows how.
"Cannot find module @cosyte/hl7"
Each format subpath needs its parser installed, as an optional peer dependency:
@cosyte/synth/hl7 needs @cosyte/hl7, @cosyte/synth/fhir needs @cosyte/fhir, and so on. The
package root (@cosyte/synth) needs none: import from there if you only need the seeded generator
and the value providers.
@cosyte/synth/deid loads @cosyte/deid, @cosyte/hl7, @cosyte/fhir, @cosyte/ccda,
@cosyte/x12, and @cosyte/ncpdp as soon as it is imported, so all six must be installed even if
you call one loop. Installation has the table.
"instanceof is false for a generated value"
A generated CcdaDocument, or any other parser class instance, should pass instanceof against the
class your code imports from the parser. Two setups break that, each by loading a second copy of the
parser:
- Node.js older than 22.12, which this package does not support. Upgrade Node.js.
- A bundle that inlines a parser, or
@cosyte/synth, while the generator loads the installed parser. Mark@cosyte/synthand each@cosyte/*parser external; see Bundle an application that uses it.
An unsupported request threw
A generator has nothing to tolerate, so a request it cannot honor throws a SynthError with a
stable code, rather than return a mislabelled fixture:
import { defineSynthProfile } from "@cosyte/synth";
// A blank profile name is refused: this block throws a SynthError with the code
// SYNTH_INVALID_PROFILE, and the docs test checks that it does.
defineSynthProfile({ name: "" });
The codes you are most likely to see:
SYNTH_UNSUPPORTED_KIND: a message kind, document type,mixentry, variant, Bundle type, or US Core profile name outside the set that option accepts. The option's type in the API reference lists the set.SYNTH_UNSUPPORTED_QUIRK: a quirk name outside the format's registry.Object.keys(HL7_QUIRKS)(orCCDA_QUIRKS,ASTM_QUIRKS) lists them.SYNTH_PROFILE_NOT_GENERATED: a US Core profile the guide publishes and we do not generate yet.SYNTH_INTENDED_WARNING_MISMATCH: a quirk fixture did not raise exactly its intended warning, which can happen when a parser version changes what it reports.
Every message comes from the frozen SYNTH_FATAL_MESSAGES table, keyed by code. It says which rule
refused and does not quote the request: SynthError takes a code and nothing else. Branch on
err.code, and read the value you passed off your own arguments; the stack names the call.
Fatal codes lists every code.
A pairing loop threw a TypeError
x12DeidLoop({ variant }) and ncpdpTelecomDeidLoop({ transaction }) throw a plain TypeError for
a value outside their union, rather than a SynthError. Check the value against X12DeidVariant
("837P", "837I", "837D", "271", "835") or NcpdpTelecomVariant ("B1", "B2", "B3").
Is the generated output safe to commit and log?
Yes; that is the purpose of the package. Every value comes from a reserved range or the invented-name pool, and the test suite's synthetic-safety properties check that for arbitrary seeds and every format. You can commit a generated corpus as a fixture without a PHI review of its contents.
The diagnostics are safe for a different reason. Output is safe because of where its values come from. A diagnostic is safe because it holds no value at all: a fatal message is a fixed string, a selector you pass is checked against its closed set before it goes anywhere, and a round-trip result keeps only the parser's warning codes.
What that does not cover: a value you pass to a generator is your value, and it goes into the
artifact you asked for; content is the fixture, not a diagnostic. And if you hand a document you
wrote to a round-trip helper and the sibling parser cannot read it, the error you catch is that
parser's own, passed through unchanged.
Where the limits are
All six formats generate (HL7 v2, FHIR, C-CDA, X12, NCPDP, and ASTM), with vendor quirks for HL7
v2, C-CDA, and ASTM, and the pairing loop for HL7 v2, FHIR, C-CDA, X12, and NCPDP Telecom.
What it does and does not do is the full account: synth is a format and
conformance generator, not a clinical simulator; every value is synthetic by construction; output
is the same for a seed within a version window; and no terminology is bundled.