Troubleshooting
The CLI cannot load a config
Section titled “The CLI cannot load a config”Read the stable code first:
CONFIG_NOT_FOUND—--configdid not name a file.CONFIG_LOAD_FAILED— module evaluation or import resolution failed.CONFIG_EXPORT_INVALID— the module did not default-export an object.
For load failures:
- Confirm
tsconfigPathpoints at the consumer’s real alias-owning config. - Import a Node-safe contracts entry point instead of an Angular barrel.
- Keep every alias imported by that entry point in the tool config; TypeScript
pathsobjects do not merge entry by entry throughextends. - Run
pnpm exec formly-contracts listbeforegenerate.
Use the Angular-aware binary when selected project configs legitimately load partially compiled Angular code:
pnpm exec formly-contracts-angular list \ --project-config libs/forms-kit/formly-contracts.project.ts \ --explainDefault output retains the stable worker code and phase while hiding the
underlying exception. --explain requests a bounded local cause chain and only
stack frames inside the workspace. It never enables raw child stderr or writes
the explanation into generated artifacts. Review before sharing: an
application-thrown message may still contain private identifiers.
A browser barrel fails before a component is initialized
Section titled “A browser barrel fails before a component is initialized”An error such as Cannot access 'NumberComponent' before initialization is a
JavaScript module initialization failure. It is different from Angular’s
missing-JIT-compiler error and can remain after switching to
formly-contracts-angular.
If an Angular component refers to itself in decorator provider metadata, use Angular’s documented deferred reference:
import { forwardRef } from '@angular/core';
{ provide: FORM_FIELD_CONTROL, useExisting: forwardRef(() => NumberComponent),}Apply that change only when the failing edge is the component self-reference;
forwardRef is not a generic repair for every circular import. Then keep the
contract worker out of the broader browser graph by importing a dedicated
@work/forms-kit/contracts entry point. The
Node-safe Angular libraries
guide shows the full boundary and a temporary leaf-import shim.
Discovery finds the wrong files
Section titled “Discovery finds the wrong files”Project patterns are relative to the workspace root. Use
excludeProjectConfigs for legacy or generated trees. Discovery prunes
dependencies, Git metadata, and the effective output directory, but it does not
exclude every directory named dist.
Project-config symlinks are rejected. Keep real configs inside the workspace.
Discovery failures carry one of these stable codes:
CONFIG_PATH_OUTSIDE_WORKSPACE— a matched project config resolves outside the workspace root.DUPLICATE_PROJECT_ID— two discovered project configs declare the sameprojectId.DUPLICATE_SOURCE_ID— two sources within a project declare the samesourceId.PROJECT_CONFIG_SYMLINK_UNSUPPORTED— a matched project config path is a symlink.
generate or check fails
Section titled “generate or check fails”runWorkspace/checkWorkspace throw a WorkspaceGenerationError with one
of these codes:
WORKSPACE_DISCOVERY_FAILED— workspace discovery failed.PROJECT_CONFIG_RESOLUTION_FAILED— a project’s configuration failed to resolve.SOURCE_LIST_FAILED— a form contract source could not be listed.SOURCE_LIST_INVALID— a form contract source returned an invalid list.FORM_DEFINITION_INVALID— a form contract definition is invalid.DUPLICATE_FORM_ID— a form ID is declared more than once.FORM_FACTORY_FAILED— a form contract factory failed.FORM_INSTANCE_INVALID— a form contract factory returned an invalid instance.CONTRACT_EXTRACTION_FAILED— form contract extraction failed.SOURCE_USAGE_PROJECT_CONFIG_UNSUPPORTED— source indexing found a project config outside the pilot’s supported.ts,.mts, or.ctsentry points.SOURCE_USAGE_INDEX_FAILED— the configured TypeScript source-usage program could not be built or indexed within the workspace boundary.DIAGNOSTIC_POLICY_FAILED— a generated contract violates diagnostic policy (diagnostics.failOn).DEPENDENCY_SNAPSHOT_UNAVAILABLE— a pnpm dependency snapshot could not be selected.RUNTIME_PROVENANCE_UNAVAILABLE— runtime toolchain provenance could not be determined.OUTPUT_PATH_OUTSIDE_WORKSPACE— an output path is outside the workspace.OUTPUT_SYMLINK_UNSUPPORTED— symlinked output paths are not supported.OUTPUT_WRITE_FAILED— workspace contract output could not be written.
Source-usage diagnostics do not use the Form Contract diagnostics.failOn
policy. Important source diagnostics include:
SOURCE_DESCRIPTOR_UNSUPPORTED— a discovered project config or its source registration is outside the direct canonical helper grammar, has IDs that do not match runtime inventory, or contains unsupported wrapped/spread/dynamic source elements. No same-ID fallback is attempted.SOURCE_DESCRIPTOR_CONFLICT— more than one canonical registration claims the same project/source authority. Remove the duplicate; neither claim is selected by source order.SOURCE_FILE_SNAPSHOT_MISMATCH— final file bytes do not match the TypeScriptSourceFile.textthat established authority or a usage. Every exact usage depending on that file is suppressed, not just usages physically located in it.SOURCE_RUNTIME_RESOLUTION_MISMATCH— TypeScript and the exact Jiti runtime used for config evaluation selected different canonical files for a traversed authority import or re-export. Align the root resolver settings or use an import shape supported consistently by both.SOURCE_USAGE_UNSUPPORTED— a recognized callsite is unsafe for exact linkage, including unchecked JavaScript-family files and TypeScript files containing@ts-nocheck,@ts-ignore, or@ts-expect-errordirectives.
Run generate and check against a quiescent checkout and stop watchers that
rewrite authority files during the pass. The authority and application
TypeScript Programs are created before form factories execute, but the MVP does
not snapshot every
runtime/Jiti-loaded module and retains a short config-loading-to-Program
boundary. Source coverage is intentionally incomplete, so an unsupported
out-of-grammar call is not guaranteed to produce a per-call diagnostic.
A custom field is unmapped
Section titled “A custom field is unmapped”UNMAPPED_FIELD_TYPE means extraction preserved the field but had no reviewed
operational profile. Formly’s { name, component } registration cannot supply
that meaning by itself.
When a shipped preset matches the component, define one contracted type, derive its Formly registration, and lower the same definition into the project registry:
export const MONEY_INPUT_TYPE = defineContractedFormlyType({ name: 'money-input', profile: { id: 'claims.money-input', version: 1 }, behavior: typedInput({ semanticType: 'currency', role: 'spinbutton' }),});
toFormlyTypeRegistration(MONEY_INPUT_TYPE, MoneyInputComponent);
export const FIELD_PROFILES = buildFieldTypeProfileRegistry({ id: 'claims.fields', version: 1, types: [MONEY_INPUT_TYPE],});Attach FIELD_PROFILES as the owning project’s fieldTypeProfiles. If a
profile already exists, confirm its generated registration matches the exact
field type and selected variant. Declare wrapper behavior with
defineContractedFormlyWrapper(...), include it in the builder’s wrappers,
and reuse its exact name in Formly’s ordinary wrapper registration.
If no preset matches losslessly, keep an explicit reviewed legacy profile or the diagnostic. Do not map a type to the nearest native widget by appearance. See Custom field profiles for the complete paired flow and shipped preset list.
Dynamic options are empty
Section titled “Dynamic options are empty”Function- or async-backed options remain dynamic during declared extraction. Add a named synthetic scenario in a trusted Angular/Formly environment for each meaningful branch. The result is complete for that scenario only.
check reports stale artifacts
Section titled “check reports stale artifacts”Run generate, inspect the contract diff and diagnostics, and commit or publish
the new artifact set according to the consumer’s policy. Do not overwrite one
hashed contract while leaving an old workspace index in place.
Common intentional causes include field configuration changes, locator-policy changes, profile registry changes, tool/runtime provenance changes, and lockfile changes.
A Playwright helper cannot find a locator
Section titled “A Playwright helper cannot find a locator”Stop. Check the node’s locators and diagnostics. A missing locator is missing
evidence. Add an application-owned test attribute or improve a reviewed profile
and regenerate; do not fall back to an invented CSS selector.