Packages and Implementation Guides

 

Around the time FHIR R4 was released, the FHIR community standardized on the NPM (Node Package Manager) format as a standard mechanism for distributing shared FHIR artifacts. The most common use case for these packages is in distributing Implementation Guides. This page describes Smile CDR support for Implementation Guides, and the use of Packages as a distribution mechanism.

Background: Implementation Guides

 

An Implementation Guide (often called an "IG") is a profile on the FHIR specification that constrains it down for a specific set of use cases. Implementation guides can be thought of as a "mini FHIR spec" that is defined by a particular geographic region, specialty organization, project, etc.

For example, the US Core Implementation Guide describes a set of profiles on the FHIR specification in order to ensure a common data models for interoperable applications in the United States. Among other things, the US Core IG includes resources such as StructureDefinion, ValueSet, SearchParameter, etc. See Conformance Data to learn more about what these are used for.

Background: Packages

 

When an Implementation Guide is published, it takes two main forms. The first is a collection of web pages that can be browsed to learn about the specification (e.g. the US Core IG described above). The second is a collection of machine readable artifacts (i.e. the FHIR Conformance Data resources mentioned above) that can be used to validate content to ensure that it conforms with the given Implementation Guide constraints.

These resources are assembled into a Package, using the format defined by the Node Package Manager (NPM) specification.

Ingesting Packages into Smile CDR

 

Packages Overview

The Smile CDR FHIR Storage Module (RDBMS) contains a set of database tables dedicated to native storage of package contents. This allows the Conformance Data contained within a package to be stored and made available to the validator.

This FHIR Storage (RDBMS) module can ingest packages from both local files, as well as the public FHIR package registries. By default, Smile CDR fetches packages from packages2.fhir.org/packages (primary) with packages.fhir.org as a fallback (which redirects to Simplifier). Packages are stored in a dedicated set of tables in the FHIR Storage (RDBMS) module database schema. Package binaries (i.e. the actual contents of the package) are stored as Binary resources, meaning that Externalized Binary Storage will be used for storing large binary payloads if it is enabled.

For examples on how to ingest a package via a direct HTTP operation, please see the documentation for the module. Alternatively, you can use the Smile NPM Package Java Client.

Adding packages to the package registry has several purposes:

  • If the package contains conformance resources for the same version of FHIR as the FHIR Storage module, these resources become available to the FHIR validator. Note that the package registry is able to store and serve packages for other versions of FHIR, but the individual resources will not be available to the validator.

  • Package contents can be automatically extracted and saved as individual resources in the repository.

  • Packages can be served via the Package Registry Endpoint.

Allowed Package URLs (whitelist)

 

Smile CDR only fetches packages from URLs matching a configured whitelist.

As a security measure, any package install referencing a URL outside the whitelist is rejected before any download is attempted.

The whitelist is a property of the FHIR Storage module, supplied as JSON either inline or in a file:

Set one or the other. If both are set, the file takes precedence.

Example

{
  "whitelist": [ {
    "url": "https://packages2.fhir.org"
  }, {
    "url": "https://registry.internal.example.com/packages",
    "privateServer": true
  }, {
    "url": "http://mirror.internal.example.com/packages",
    "privateServer": true
  }, {
    "url": "file:///opt/smilecdr/packages"
  } ]
}

Each entry carries:

  • url – The permitted URL prefix. Must begin with one of http:, https:, file: or classpath:. An entry using any other scheme is ignored.

  • privateServer – Whether this entry may resolve to an address on a private network. Defaults to false. Package fetches that resolve to a private or loopback address are blocked unless the matching entry sets this to true. Set it for a mirror or registry hosted inside your own network.

Note: privateServer applies to http: and https: entries only. It is ignored for file: and classpath: entries.

Whether plain HTTP is permitted is taken from the entry's own url rather than configured separately: an https: entry refuses plain-HTTP fetches against that host, and an http: entry permits them.

Remote entries are matched by origin, and local entries are matched by resolved path.

So file:///opt/smilecdr/packages permits any package beneath that directory but not one in /opt/smilecdr/packages-other.

Write file: URLs as either file:/opt/smilecdr/packages or file:///opt/smilecdr/packages.

Both resolve to the same path. But the two-slash spelling file://opt/smilecdr/packages parses opt as a hostname and whitelists /smilecdr/packages instead of the intended directory.

The whitelist for remote URLs is applied cluster-wide rather than per module, so two FHIR Storage modules in the same cluster should not be given different values.

If they are, the last module to start determines the whitelist that is applied.

If a package fails to install because its URL is not whitelisted, see Blocked package URL.

Default Whitelist

By default, the following whitelist is provided:

{
	"whitelist": [
		{
			"url": "https://simplifier.net"
		},
		{
			"url": "https://hl7.org"
		},
		{
			"url": "https://packages.fhir.org"
		},
		{
			"url": "https://packages2.fhir.org"
		},
		{
			"url": "file:///opt/smilecdr/packages"
		},
		{
			"url": "classpath://package-loading"
		},
		{
			"url": "classpath://packages/"
		}
	]
}

This can be updated by changing the appropriate property in the FHIR Storage module. Changes take effect after module restart.

Leaving both whitelist properties empty is treated the same as never having set them, and the default above applies. Clearing the property therefore does not block package loading.

To refuse every package URL, supply an explicit empty list instead:

{
	"whitelist": []
}

No entry can match an empty list, so every package install fails with HAPI-3028. A whitelist JSON document with no whitelist property behaves the same way.

Redirects

A whitelist entry is matched against the URL that Smile CDR requests. If a registry answers with a redirect, the redirect target is matched against the whitelist in turn before it is followed.

Up to a maximum of five redirect hops are followed; beyond that the install fails.

A redirect is refused if the target is not on the whitelist, if it would downgrade https: to http:, if it repeats a URL already visited in the chain, or if the response carries no usable Location header.

A registry or mirror that redirects — for example to a CDN — therefore needs both the URL you request and the URL it redirects to on the whitelist.

Package Pre-Seed Installation

 

When Smile CDR starts, the FHIR Storage module can be configured to automatically import packages into the local registry. This makes the conformance artifacts in these packages available to the validator. It can also optionally import resources from the package directly into FHIR Storage, so that these resources become available for FHIR searches, reads, etc.

See Pre-Seeding Packages and FHIR Resources for information on how to do this.

Pre-Seeding Resources into the Repository

 

Packages can also be used to seed a set of data into the FHIR repository itself (i.e. not simply making these resources available to the validator, but actually storing them in the database so that they are standard FHIR resources that can be searched, read, etc.). This is called Store And Install mode. This functionality is not limited to validation/conformance resources: It can also be used to seed a new environment with a common set of Organization resources for a project, to seed SearchParameters to every environment, etc.

Note that using the Store and Install mode is not required in order to perform resource validation. Regardless of the ingestion mode, package content is stored independently of FHIR clinical resources in a dedicated package storage for subsequent retrieval in validation. This mode is only needed if you want to store the FHIR resources in the repository and use a FHIR REST API to query them.

Resources loaded in pre-seeding must include either a url or identifier element, which is used to ensure that the package loader doesn't create duplicate resources.

A sample spec file is shown below:

{
  "name" : "com.example.my-resources",
  "version" : "1.0",
  "packageUrl" : "classpath:/my-resources.tgz",
  "installMode" : "STORE_AND_INSTALL",
  "installResourceTypes" : [ "Organization", "Medication", "PlanDefinition", "SearchParameter" ],
  "fetchDependencies" : false,
  "versionPolicy" : "MULTI_VERSION"
}

Customizing Installed Resource Types

 

The installResourceTypes parameter controls which resource types are extracted from a package and installed. When omitted, a default set of conformance types is installed. See the Package Registry Endpoint Module for the full parameter reference including the default list.

Note that specifying installResourceTypes overrides the defaults rather than extending them. When adding types, ensure you include the default conformance types as well.

Depending on the IGs and your use case, you may need to install additional resource types beyond the defaults. Common candidates include:

Conformance resource types define the rules, capabilities, and constraints of a FHIR server. See Conformance Data for details.

  • OperationDefinition declares custom operations defined by the IG
  • CapabilityStatement declares server capabilities
  • CompartmentDefinition defines compartment boundaries
  • ImplementationGuide is the IG resource itself (useful for tracking which IGs are installed)

Clinical reasoning and workflow resource types define clinical logic, quality measures, and structured workflows. See the CQL Module and DQM Module documentation for how these are used in Smile CDR.

  • Questionnaire defines structured data capture forms
  • PlanDefinition defines clinical protocols and order sets
  • ActivityDefinition defines clinical activities
  • Measure defines quality measures (relevant to CMS reporting)
  • Library contains CQL or other logic libraries referenced by Measure and PlanDefinition resources

Creating Packages

 

Packages are available from the public FHIR package registries. You can also create your own using the smileutil Create Package Command.

Troubleshooting Package Installation

 

Broken IG dependencies

When fetchDependencies is enabled, the install operation automatically downloads and installs all transitive dependencies declared by the IG package. If a dependency package is malformed, has been removed from the registry, or contains resources that fail to parse, the install operation will fail.

How to identify a broken dependency:

The install operation response will contain error messages that name the problematic package. Messages prefixed with a HAPI-XXXX code can be searched in the application logs. Look for messages such as:

  • HAPI-2915: Unable to retrieve NPM package <package_name>#. The package could not be downloaded from the registry (e.g. it has been unpublished or the version does not exist). Only appears in async mode.
  • HAPI-2916: Error occurred while retrieving package. The package was downloaded but could not be parsed or processed. Only appears in async mode.
  • HAPI-1301: Unable to locate package <package_name>#. The package was not found in any configured registry. Only appears in sync mode.
  • HAPI-2955: Failed to process dependencies for package <package_name>#: . The dependency resolution step itself failed. Only appears in async mode. The cause originates from external libraries and will not have a HAPI code.
  • Failed to launch child job for dependency package <package_name>#. Skipping this dependency. Cause: . This means a specific dependency could not be installed, though the parent install may continue. Only appears in async mode. The Cause suffix contains the original error with its own HAPI code (e.g. HAPI-1301, HAPI-2065).

Workaround: exclude the broken dependency and install it separately

This example uses <broken_dependency> as the problematic package, but the pattern applies to any dependency.

  • Add "dependencyExcludes" to all install operations to skip the problematic package. The values are treated as regex patterns matched against the dependency package ID. See Dependency Excludes for details and examples.
{
  "name": "<package_name>",
  "version": "<version>",
  "installMode": "INSTALL_ONLY",
  "fetchDependencies": true,
  "dependencyExcludes": ["<broken_dependency>"]
}
  • Install the excluded dependency separately at a known-good version. Keep fetchDependencies enabled so that the known-good version's own dependencies are still resolved:
{
  "name": "<broken_dependency>",
  "version": "<known_good_version>",
  "installMode": "INSTALL_ONLY",
  "fetchDependencies": true
}

If the known-good version itself has problematic sub-dependencies, set "fetchDependencies": false as a last resort, but be aware that its own dependencies will not be installed.

When fetchDependencies is enabled and the dependency tree pulls in multiple versions of the same package, the installer detects and skips redundant older versions to avoid overwriting already-installed resources. See Redundant Dependency Handling in the HAPI FHIR documentation for details on how this behaves differently under SINGLE_VERSION vs MULTI_VERSION policy.

Blocked package URL

If the package URL is not covered by the configured whitelist, the install fails before any network request is made.

How to identify a blocked URL:

Look for messages such as:

  • HAPI-3028: Attempting to request from non-whitelisted path . The URL is well-formed and uses a supported scheme, but no whitelist entry matches it.
  • HAPI-3029: Unrecognized scheme. The URL uses a scheme other than http:, https:, file: or classpath:. These are the only schemes the package loader supports, and no whitelist entry can permit any other scheme.
  • Refusing redirect from <url1> to non-whitelisted URL <url2>. The requested URL was permitted but the server redirected to one that is not. See Redirects.

Because the rejection happens before the download, this can be mistaken for a network failure. The distinguishing sign is that the failure is immediate and no connection is attempted.

Resolution:

Add an entry covering the URL to the FHIR Storage module whitelist and restart the module. See Allowed Package URLs.

If nothing installs at all, check that the whitelist is configured. If no whitelist is configured, all URLs are rejected.

If the URL looks like it should already match a file: entry, check the number of slashes after the scheme. file://opt/packages whitelists /packages on a host named opt, not the directory /opt/packages; write file:/opt/packages or file:///opt/packages instead.

Network or connection failures

If the Smile CDR server cannot reach the public NPM registry (e.g. packages.fhir.org) due to network restrictions, DNS resolution failures, or transient connectivity issues, the package install operation will fail during the download phase.

How to identify a network failure:

Look for messages such as:

  • HAPI-1303: Received HTTP <status_code> from URL: . The registry was reachable but returned an error status (e.g. 404 for a missing package, 503 for a temporary outage).
  • HAPI-1304: Error loading "": <error_message>. The HTTP request itself failed. The error_message will contain details such as Connection refused, Connection timed out, UnknownHostException, or similar network-level errors.

Workarounds:

  • Transient errors (timeouts, 503): retry the install operation after a short delay. There is no built-in retry for the download step.
  • Firewall or proxy restrictions: ensure the server can reach packages.fhir.org (or your configured registry URL) on port 443. If the server is behind a proxy, configure the JVM proxy settings (-Dhttp.proxyHost, -Dhttps.proxyHost, etc.).
  • Offline or air-gapped environments: see Offline and air-gapped installation below.

Offline and air-gapped installation

Download each required package from the public FHIR package registries on a machine with internet access, transfer the .tgz files to the server, then install using packageUrl with a file: or classpath: URL. See the packageUrl property reference for supported URL schemes and examples.

The file: or classpath: URL must also be covered by the Allowed Package URLs (whitelist). The directory the packages are staged in needs a whitelist entry of its own, otherwise the install is rejected with HAPI-3028 before the file is read.

Keep fetchDependencies set to false (the default). If enabled, the installer will attempt to fetch transitive dependencies from the public registry, which will fail. Install each dependency package separately instead.

{
  "name": "hl7.fhir.us.core",
  "version": "5.0.1",
  "packageUrl": "file:///data/packages/hl7.fhir.us.core-5.0.1.tgz",
  "installMode": "INSTALL_ONLY",
  "fetchDependencies": false
}

FHIR version mismatch

If a package (or one of its dependencies) declares a FHIR version that is incompatible with the server, the installer will attempt to find a version-specific variant on a best-effort basis (e.g. hl7.fhir.uv.extensions.r4 instead of hl7.fhir.uv.extensions). If no variant is found, the install operation will fail.

How to identify a version mismatch:

Look for a message such as:

  • HAPI-1288: Cannot install implementation guide: FHIR versions mismatch (expected <=<server_version>, package uses <package_version>)

This typically occurs when a dependency transitively pulls in a package built for a different FHIR version (e.g. R5 resources installed on an R4 server) and no version-specific variant exists.

Workarounds:

  • Use dependencyExcludes to skip the incompatible dependency, then install a version-appropriate replacement manually (same approach as Broken IG dependencies).
  • Verify that the correct version of the IG is being installed for your server's FHIR version. Many IGs publish separate packages per FHIR version (e.g. .r4 suffix).

For a detailed explanation of the automatic version-specific substitution logic (how the .r4 suffix variant is selected), see Cross-Version Package Dependencies in the HAPI FHIR documentation.

Parallel IG installation conflicts

When IGs are installed in parallel under SINGLE_VERSION mode, concurrent installs can conflict. The second install logs the following at ERROR level and continues:

Concurrent install conflict on resource [<id>]: update skipped (incoming: [<url>], existing: [<existing_url>]). Cause: <cause>

To avoid this, use "versionPolicy": "MULTI_VERSION" (the default). See Concurrent Install Conflicts in the HAPI FHIR documentation.

Referential integrity errors when installing non-conformance resources (HAPI-1094)

When installing non-conformance resources (e.g. Patient, Organization) from additional resource folders, the installer may fail with HAPI-1094 if a resource references another resource that has not yet been stored and the Auto-Create Placeholder Reference Targets setting is not enabled.

What to do:

Enable the Auto-Create Placeholder Reference Targets setting on the FHIR Storage module before installing. This allows the installer to create empty stubs for unresolved reference targets, which are overwritten when the referenced resource is installed. See Additional Resource Folders for configuration details.

Identifying CodeSystems that need to be uploaded

After installing Implementation Guides, some ValueSets may fail to expand because they reference CodeSystems that are not yet loaded, i.e. CodeSystems where only a content=not-present placeholder exists but no concepts have been uploaded (see How Uploaded Terminology Interacts with IG Packages for how these placeholders work). This section describes how to identify which CodeSystems are missing so you can upload them.

Using ValueSet expansion failures:

The most common signal is a ValueSet that fails to pre-expand. The application logs will contain messages such as:

HAPI-0702: Unable to expand ValueSet because CodeSystem has CodeSystem.content=not-present but contents were not found: http://hl7.org/fhir/sid/icd-10-cm

See Troubleshooting Expansion Failures for how to find all failed ValueSets and their failure reasons.

Searching for not-present CodeSystems:

CodeSystems that are referenced by installed IGs but have no concepts loaded are stored with content=not-present. Use the standard FHIR content-mode search parameter to find them (this requires Support Default SearchParameters to be enabled on the FHIR Storage module):

GET [base]/CodeSystem?content-mode=not-present

Not all CodeSystems listed need to be loaded, only the ones required for your use case or causing ValueSet expansion failures. See Uploading CodeSystems for upload methods and formats.