Common errors and fixes.
Seven known issues with exact root causes and fixes. If you encounter an error not listed here, check the profile definition at fhir.cognovis.de/praxis/ or contact the maintainers.
Organization: address.type must be "both"
Symptom
Aidbox returns 422 Unprocessable Entity with a slice validation error on Organization.address when type is absent or set to "postal" or "physical".
Root cause
KBV_PR_FOR_Organization defines a mandatory address slice "Strassenanschrift" discriminated by Address.type = "both". Aidbox enforces this slice cardinality (min=1, max=1).
Fix
// The buildOrganization builder defaults type to 'both' when not provided.
// Do not override it with 'postal' or 'physical'.
import { buildOrganization } from '@polaris/fhir-de'
const org = buildOrganization({
id: 'org-001',
name: 'Musterpraxis',
address: {
line: ['Praxisstraße 1'],
city: 'Berlin',
postalCode: '10115',
// Do not pass type: 'postal' — this breaks the KBV slice
// type: 'both' is the default and must not be changed
},
}) birthDate: absent key vs empty string
Symptom
Aidbox rejects the Patient resource with a 422 error mentioning an invalid birthDate format, even when the date looks correct.
Root cause
If birthDate is set to an empty string (""), Aidbox's FHIR validator sees an invalid date format and rejects the resource. The birthDate field must be completely absent from the resource when not known.
Fix
import { buildPatient } from '@polaris/fhir-de'
// Correct: do not pass birthDate at all when not known
const patient = buildPatient({
id: 'pat-001',
firstName: 'Anna',
lastName: 'Müller',
// birthDate: undefined ← omit the key entirely
// birthDate: '' ← this causes 422
})
// buildPatient() handles this automatically:
// it deletes the birthDate key when the value is falsy.
// If you build the resource manually, use:
const resource = { resourceType: 'Patient', name: [{ use: 'official', family: 'Müller' }] }
// Do NOT add: resource.birthDate = '' meta: {} as any pattern in profile.create()
Symptom
TypeScript compilation error when passing { meta: {} } to a profile's create() or createResource() method — the meta type is too strict.
Root cause
Generated profile class createResource() methods require a meta object with a specific branded type. The caller does not have access to this type directly. Passing {} as any is the established pattern to satisfy the type constraint while letting the builder populate meta correctly.
Fix
import { KBV_PR_FOR_PatientProfile } from '@polaris/fhir-de'
// Use the 'as any' cast when meta is required by the profile class
const resource = KBV_PR_FOR_PatientProfile.create({
id: 'pat-001',
meta: {} as any, // branded Meta type — 'as any' is intentional
name: [{ use: 'official', family: 'Müller' }],
birthDate: '1985-04-12',
}).toResource()
// Note: the resource builders (buildPatient, buildOrganization, etc.)
// handle this internally — you should not need meta: {} as any
// when using the builder functions. No default transport: FhirTransport must be provided
Symptom
createFhirDeClient throws at construction time or at the first operation because no transport is configured.
Root cause
The SDK does not bundle a default HTTP client. The FhirTransport SPI is required — this is an intentional design decision (arch-council F#2) to keep the SDK free of HTTP library dependencies.
Fix
import { createFhirDeClient, FhirDeHttpError } from '@polaris/fhir-de'
import type { FhirTransport } from '@polaris/fhir-de'
// You must implement FhirTransport
const baseUrl = 'https://aidbox.example.com/fhir'
const transport: FhirTransport = {
async request(method, path, init) {
const normalizedPath = path.startsWith('/') ? path : `/${path}`
const url = new URL(`${baseUrl.replace(/\/$/, '')}${normalizedPath}`)
if (init?.query) {
for (const [key, value] of Object.entries(init.query)) {
const values = Array.isArray(value) ? value : [value]
for (const item of values) url.searchParams.append(key, item)
}
}
const res = await fetch(url, {
method,
headers: {
'Content-Type': 'application/fhir+json',
Accept: 'application/fhir+json',
'Authorization': `Bearer ${process.env.AIDBOX_TOKEN}`,
...init?.headers,
},
body: init?.body == null ? undefined : JSON.stringify(init.body),
})
const body = res.status === 204 ? null : await res.json().catch(() => null)
if (!res.ok) throw new FhirDeHttpError(res.status, body)
return body
},
}
// Then pass it to createFhirDeClient
const client = createFhirDeClient({
baseUrl,
transport,
}) FhirDeProfileMismatchError on from()
Symptom
Calling ProfileClass.from(resource) throws FhirDeProfileMismatchError even though the resource looks correct.
Root cause
Profile.from() performs strict validation: it checks that meta.profile includes the profile's canonical URL, and validates required fields. If the resource was created by a different profile or is missing meta.profile, from() will reject it.
Fix
import {
KBV_PR_FOR_PatientProfile,
FhirDeProfileMismatchError,
} from '@polaris/fhir-de'
try {
// from() is strict: validates meta.profile + required fields
const profile = KBV_PR_FOR_PatientProfile.from(rawResource)
} catch (err) {
if (err instanceof FhirDeProfileMismatchError) {
// Resource does not match the profile
// Options:
// 1. Use apply() instead — less strict, sets meta.profile without full validation
const profile2 = KBV_PR_FOR_PatientProfile.apply(rawResource)
// 2. Fix the resource: ensure meta.profile includes the canonical URL
// 3. Use the correct profile class for this resource type
}
}
// apply() vs from():
// from() — strict validation, throws on mismatch
// apply() — sets meta.profile, no full validation (use for migration) POLARIS_ANON_SALT: environment variable is not set
Symptom
createFhirDeClient (or new AnonymizingTransport) throws synchronously at startup with the message "AnonymizingTransport: POLARIS_ANON_SALT environment variable is not set."
Root cause
piiMode: 'anonymized' was passed to createFhirDeClient, but the POLARIS_ANON_SALT environment variable is not defined in the process environment. The constructor fails loudly by design (fail-closed principle) to prevent silent data leaks from misconfigured deployments.
Fix
# Set the env var before starting your process:
export POLARIS_ANON_SALT="your-secret-salt-string"
# Or in a .env file (loaded by Bun automatically):
POLARIS_ANON_SALT=your-secret-salt-string
# In tests you can pass the salt explicitly instead of using the env var:
import { AnonymizingTransport } from '@polaris/fhir-de'
const anonTransport = new AnonymizingTransport(transport, 'test-salt')
const client = createFhirDeClient({ baseUrl, transport: anonTransport })
// Note: the salt must remain stable across deployments.
// Changing it changes all hashed IDs — anon-<hash> values are no longer
// consistent with previously returned hashes. Registry: 401 or package not found from npm.cognovis.de
Symptom
bun install (or npm install) fails with a 401 Unauthorized or package-not-found error when resolving @polaris/fhir-de.
Root cause
The most common cause is a missing @polaris scope mapping in .npmrc or bunfig.toml. The npm.cognovis.de registry is read-only anonymous — no authentication token is required for read access. Without the scope mapping, the package manager looks for @polaris/fhir-de on the public npm registry (where it does not exist), resulting in a 401 or ENOT FOUND error.
Fix
Option A — .npmrc
@polaris:registry=https://npm.cognovis.de Option B — bunfig.toml
[install.scopes]
"@polaris" = "https://npm.cognovis.de" # Step 2: Verify the package resolves correctly (no auth token needed):
curl -s https://npm.cognovis.de/@polaris/fhir-de | jq '."dist-tags".latest'
# Step 3: Re-run the install:
bun install
# Note: npm.cognovis.de is a read-only anonymous registry.
# No authentication token is required. Do not add _authToken
# entries for this registry — they are not needed and will not help. Still stuck?
Check the profile definition or reach out.
If the error is a 422 from Aidbox and you cannot determine the root cause from the response body, check the full profile definition at fhir.cognovis.de/praxis/index.html. The StructureDefinition will show the exact cardinality constraints and slice discriminators causing the validation failure.