Lint Your Pack
Validate packs in KDataForge Pack Manager or with the kdf lint command, including pack configuration, templates and cold properties; reproduce existing KPatchwork CI checks with the standalone linter.
Recommended: KDataForge Pack Manager
Section titled “Recommended: KDataForge Pack Manager”For local pack authoring, use KDataForge Pack Manager instead of
installing the standalone Python linter. Open your project, edit YAML in the Code or Visual editor,
and review live diagnostics in Problems. For a full project check, choose Lint pack under
Settings → Language server. An optional .kdfdata reflection archive adds class and property
information. After validation, test the pack in game with
/kdf report.
KPatchwork’s pull-request CI still uses the pinned Python linter and repository-specific policy schemas. Use the commands below when reproducing that existing CI result. The desktop app validates KDataForge runtime grammar, so a passing desktop check does not replace KPatchwork’s policy check.
Command line: kdf lint
Section titled “Command line: kdf lint”The checks of the desktop app also run without the window. kdf lint <path> is the command-line
front end of the same validation core: it reads the same schemas and applies the same rules as the
Problems panel, including everything in
Configuration, template and cold-property checks
below.
kdf lint <your-mod>kdf lint <your-mod> --format githubkdf lint <your-mod> --datapack C:\Path\2026-10-12.kdfdatakdf lint <your-mod> --warnings-as-errors| Option | Meaning |
|---|---|
--format text, json, github, sarif | Output format. github produces workflow annotations, sarif code-scanning reports. |
--datapack <file.kdfdata> | A reflection export archive. It enables the class, property, function and setting checks. Without it those checks are skipped instead of guessed. |
--warnings-as-errors | Exit with a failure status on warnings too. |
--allow-unknown-types | Do not report root types that no schema knows (third-party handlers). |
--schema-dir <dir>, --allow-schema-override | Extra schemas, and permission for them to replace bundled ones. |
--user-settings <file> | A settings file with the lint policy (see .kdf-editor.toml in Projects and Packs). |
--profile runtime, legacy | runtime (default) validates the current runtime grammar. legacy adds the KPatchwork policy rules, for example the required contributer field. |
--no-color | Plain output. |
<path> follows the same root rules as the Python linter below: pass the folder that contains the
DataForge folder (or a folder of several nested packs). The command exits with status 1 when it
reports an error.
Configuration, template and cold-property checks
Section titled “Configuration, template and cold-property checks”These rules were added with pack configuration. They run
in the desktop app, in kdf lint, and in the language server.
| Area | Codes | What is checked |
|---|---|---|
| Config definitions | config.filename, config.filename-type, config.duplicate-id, config.prefix-clash, config.id-reserved, config.default-invalid, config.range-invalid, config.option-invalid, config.category-missing, config.category-invalid, config.reserved-key, plus schema errors such as a missing name or description | File naming, unique and well-formed ids, defaults that satisfy their type, ranges, options, categories that are UFGCategory classes |
| References | config.unknown-id, config.unknown-pack, config.pack-disabled, config.case-mismatch, config.type-mismatch, config.ref-not-resolved | Every cfg: and {cfg:} reference points at an existing field, the field type fits the destination, nothing sits where it is never resolved |
| Expressions | config.expression-syntax, config.expression-type, config.expression-quote, config.condition-invalid, config.condition-in-config | config: conditions parse, type-check and are quoted safely |
| Templates | template.unquoted-start, template.unbalanced, template.malformed, template.unknown-provider, template.nesting-depth, and with a reflection export template.unknown-member, template.member-not-callable, template.argument-count, template.argument-type, template.class-family | Token syntax, nesting depth, known providers, and for apply-time providers whether the member exists and is callable with those arguments |
| Hot and cold | config.hot-in-cold-stage, config.hot-in-condition, config.hot-into-cold-property, property.cold | Hot values used where only load-time values apply, and operations that target cold or newActors properties (needs a reflection export) |
| Game settings | setting.unknown-id, setting.type-mismatch | session:, ags: and option: ids against the settings index of a reflection export (only when it contains one) |
| Pack icon | pack.icon-missing, pack.icon-escape, pack.icon-format, pack.icon-symlink, pack.icon-path | The icon file exists, stays inside the pack, has a supported format, and is not a symlink out of the pack |
property.cold is information by default. Set coldProperties under [lint] in
.kdf-editor.toml to "info", "hint" or "off" to change it. Severities of the other codes
follow the table on the templates and
expressions pages.
Standalone Python linter for existing CI
Section titled “Standalone Python linter for existing CI”Standalone KDataForge Linter validates
packs with bundled schemas. Its CLI uses a lint subcommand plus a path:
kdataforge-linter lint DataForgekdataforge-linter lint DataForge --format githubkdataforge-linter lint DataForge --format jsonkdataforge-linter lint DataForge --allow-unknown-typesUseful options include --schema-dir <dir> for extra schemas, --allow-schema-override when a
custom schema intentionally replaces a bundled one, --allow-unknown-types for handler types that
the linter does not know yet, --warnings-as-errors, and --format text|json|github.
It also checks instanced FGUnlock and FGAvailabilityDependency references in schematics,
research trees, and delivery-task data assets. Native classes with known Blueprint implementations,
abstract classes without Blueprint variants, wrong-family references, and unknown class paths emit
warnings only. They do not turn an otherwise valid pack into exit code 1 unless you enable
--warnings-as-errors.
Base-Game Unlock Classes, Mod Unlock Classes, and Availability Dependency Classes document bundled inventory.
KPatchwork policy schemas
Section titled “KPatchwork policy schemas”KPatchwork repository runs this same
kdataforge-linter executable, pinned through its pyproject.toml and uv.lock. Repository-local
schemas under ci/schemas override selected bundled schemas with KPatchwork policy, such as its
required player-facing pack description.
The linter is also the validation step used by KPatchwork’s pull-request CI. It reports every issue
it finds in one run: warnings are printed but do not fail the command, while errors produce exit
code 1.
Run KPatchwork’s exact CI-equivalent command from its repository root:
uv run --locked kdataforge-linter lint DataForge --schema-dir ci/schemas --allow-schema-override --format githubGet the linter
Section titled “Get the linter”Install uv and Git, then clone KPatchwork:
git clone https://github.com/Satisfactory-KMods/KPatchwork.gitcd KPatchworkKPatchwork currently requires Python 3.11 or newer. uv run --locked creates the required Python
environment from the repository’s lockfile, so you do not need to install PyYAML manually.
Choose the correct root
Section titled “Choose the correct root”The positional <path> after lint is the directory searched recursively for pack.yml files and
YAML documents. Every manifest must be at least one directory below that path.
For the standard standalone layout from Getting Started,
pass <your-mod>, not its DataForge child:
<your-mod>/ DataForge/ pack.yml faster-smelting.cdo.ymlFrom the cloned KPatchwork directory, its pinned executable can also lint an external mod without the repository policy override:
uv run --locked kdataforge-linter lint "<your-mod>"For a repository containing several nested packs, pass their common container instead:
| Pack layout | <path> value |
|---|---|
<your-mod>/DataForge/pack.yml | <your-mod> |
<your-mod>/DataForge/<pack-name>/pack.yml | <your-mod>/DataForge |
KPatchwork/DataForge/<category>/<pack>/pack.yml | DataForge |
The linter examines every .yml and .yaml below the selected root. Choose the narrowest root that
still leaves at least one directory between the root and each pack.yml; otherwise unrelated YAML
files may be reported as being outside a pack.
KPatchwork policy fields
Section titled “KPatchwork policy fields”When preparing a pack for KPatchwork, add your GitHub login or a non-empty list of co-maintainers:
ref: MyTweaksname: My Tweaksversion: 1.0.0description: Explains player-facing compatibility changes supplied by this pack.contributer: - your-github-loginKPatchwork CI uses this value for ownership checks as well as linting. Changing the field in that repository requires maintainer approval.
What current standalone linter checks
Section titled “What current standalone linter checks”The current linter checks:
- YAML syntax, empty documents, and explicit
typevalues in multi-document files; - pack manifests, semver-like versions, unique refs, dependencies, missing dependencies, and dependency cycles;
- known document types and the filename-based
<name>.<type>.ymlconvention; - conditions, patch selectors, property paths, operation names, and required operation arguments;
- inline instances and generated IDs, including duplicate IDs within a pack;
- generated classes, unlocks, items, resources, buildings, recipes, schematics, and research-tree mutations;
- top-level
remove:for recipe/schematic/research documents, including remove-only documents; sublevelblocking documents andresourcenoderemoval documents;- float/vector curve keys, modes, finite values, and duplicate key times;
- included files, image assets, data assets, localization entries, game tags, and sink-point entries;
- symlinks, YAML outside a pack, and files that escape the selected root or pack directory.
Unknown condition keys are warnings. Unknown document root types are warnings only when you pass
--allow-unknown-types; otherwise the standalone linter reports them as errors because no schema
was available. Structural problems, invalid values, missing files, and duplicate identities are
errors.
Version notes
Section titled “Version notes”The standalone Python linter does not know pack configuration: *.config.yml definitions, cfg:
references, templates, config: conditions and the pack icon are checked by the desktop app and
kdf lint only. It may report a config document as an unknown root type; validate such packs
with the desktop app or kdf lint.
Use a current standalone KDataForge Linter build when using newer runtime handlers. Current bundled
schemas cover type: sublevel, type: resourcenode, and top-level remove: on type: recipe,
type: schematic, and type: research, including remove-only documents.
Older linter releases may report those documents as unknown root types or reject remove: as an
unexpected field. In that case, update the linter before treating the diagnostic as authoritative.
After linting, still verify in game with /kdf report; static schemas cannot prove runtime class
resolution or another mod’s installed content.
KPatchwork may pin an older standalone release until its lockfile is updated. Its ci/schemas
directory overrides only repository-policy schemas; it does not add every newer runtime handler.
Use a current standalone build when checking new KDataForge grammar outside KPatchwork CI.
Read the result
Section titled “Read the result”A successful run prints the number of packs and documents it checked:
lint passed: 1 pack(s), 1 YAML document(s)Errors include the file and the failed rule. Fix every error: line, then run the same command
again:
error: /path/to/pack.yml: version '1.0' is not semver-likeerror: /path/to/faster-smelting.cdo.yml: cdo.patches[0] requires propertiesThe linter has no automatic-fix mode. Show current CLI options with:
uv run --locked kdataforge-linter lint --helpWhat static linting cannot prove
Section titled “What static linting cannot prove”The linter parses files without loading Unreal Engine or Satisfactory. It cannot prove that an asset or class path exists, that a reflected property has the expected runtime type, that another mod is installed, or that two otherwise valid packs produce the intended result together.
After linting, start the game and use
/kdf report to check
runtime resolution and application. Use
debug logging when you need the before/after
value for each applied operation.
