Skip to content
k-mods.com

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.

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.

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 github
kdf lint <your-mod> --datapack C:\Path\2026-10-12.kdfdata
kdf lint <your-mod> --warnings-as-errors
OptionMeaning
--format text, json, github, sarifOutput 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-errorsExit with a failure status on warnings too.
--allow-unknown-typesDo not report root types that no schema knows (third-party handlers).
--schema-dir <dir>, --allow-schema-overrideExtra 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, legacyruntime (default) validates the current runtime grammar. legacy adds the KPatchwork policy rules, for example the required contributer field.
--no-colorPlain 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.

AreaCodesWhat is checked
Config definitionsconfig.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 descriptionFile naming, unique and well-formed ids, defaults that satisfy their type, ranges, options, categories that are UFGCategory classes
Referencesconfig.unknown-id, config.unknown-pack, config.pack-disabled, config.case-mismatch, config.type-mismatch, config.ref-not-resolvedEvery cfg: and {cfg:} reference points at an existing field, the field type fits the destination, nothing sits where it is never resolved
Expressionsconfig.expression-syntax, config.expression-type, config.expression-quote, config.condition-invalid, config.condition-in-configconfig: conditions parse, type-check and are quoted safely
Templatestemplate.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-familyToken syntax, nesting depth, known providers, and for apply-time providers whether the member exists and is callable with those arguments
Hot and coldconfig.hot-in-cold-stage, config.hot-in-condition, config.hot-into-cold-property, property.coldHot values used where only load-time values apply, and operations that target cold or newActors properties (needs a reflection export)
Game settingssetting.unknown-id, setting.type-mismatchsession:, ags: and option: ids against the settings index of a reflection export (only when it contains one)
Pack iconpack.icon-missing, pack.icon-escape, pack.icon-format, pack.icon-symlink, pack.icon-pathThe 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 KDataForge Linter validates packs with bundled schemas. Its CLI uses a lint subcommand plus a path:

kdataforge-linter lint DataForge
kdataforge-linter lint DataForge --format github
kdataforge-linter lint DataForge --format json
kdataforge-linter lint DataForge --allow-unknown-types

Useful 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 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:

Terminal window
uv run --locked kdataforge-linter lint DataForge --schema-dir ci/schemas --allow-schema-override --format github

Install uv and Git, then clone KPatchwork:

Terminal window
git clone https://github.com/Satisfactory-KMods/KPatchwork.git
cd KPatchwork

KPatchwork 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.

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.yml

From the cloned KPatchwork directory, its pinned executable can also lint an external mod without the repository policy override:

Terminal window
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.ymlDataForge

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.

When preparing a pack for KPatchwork, add your GitHub login or a non-empty list of co-maintainers:

ref: MyTweaks
name: My Tweaks
version: 1.0.0
description: Explains player-facing compatibility changes supplied by this pack.
contributer:
- your-github-login

KPatchwork CI uses this value for ownership checks as well as linting. Changing the field in that repository requires maintainer approval.

The current linter checks:

  • YAML syntax, empty documents, and explicit type values 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>.yml convention;
  • 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;
  • sublevel blocking documents and resourcenode removal 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.

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.

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-like
error: /path/to/faster-smelting.cdo.yml: cdo.patches[0] requires properties

The linter has no automatic-fix mode. Show current CLI options with:

Terminal window
uv run --locked kdataforge-linter lint --help

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.