36 KiB
Validating profiles
./scripts/check_profile.sh # everything CI runs
./scripts/check_profile.sh --vendor "<Vendor>" # development loop
./scripts/check_profile.sh profile_tool validate_slice # named checks only
scripts\check_profile.bat :: the same three, on Windows
scripts\check_profile.bat -Vendor "<Vendor>"
scripts\check_profile.bat profile_tool validate_slice
check_profile.bat is a shim around check_profile.ps1: same checks, same order, same logs. Its flags
take PowerShell spellings (-Vendor, -ProfilesDir, -Validator, -Download, -Refresh,
-WorkDir, -LogLevel), and positional check names are unchanged. -p, -v and -l are aliases,
so -v Elegoo -l 2 reads the same on both platforms. It passes -ExecutionPolicy Bypass because a
default Windows client refuses to run a checked-out .ps1 at all. The .ps1 finds Python itself,
probing py -3, then python, then python3; run the tool by hand with py -3 for the same reason.
Every check runs even after an earlier one fails; the script exits non-zero if any failed, and writes
logs/<check>.log plus, on failure, pr_comment.md (the same report CI posts on the PR) under a
per-user cache dir:
| Platform | Cache dir |
|---|---|
| macOS | ~/Library/Caches/orca-profile-check |
| Linux | ${XDG_CACHE_HOME:-~/.cache}/orca-profile-check |
| Windows | %LOCALAPPDATA%\orca-profile-check |
It is named apart from OrcaSlicer's own per-user dirs and sits outside the checkout, so every worktree
shares one copy and each run overwrites its logs/. --work-dir / -WorkDir overrides it.
When other worktrees or agents may run checks too, pass --work-dir <a dir of your own> from the start
and capture the console output yourself: the shared logs/ can belong to another run by the time you
read them. With --work-dir, also pass --validator pointing at the cached nightly, so the new dir
does not download it again:
| Platform | Cached validator |
|---|---|
| Linux | <cache dir>/validator/OrcaSlicer_profile_validator |
| macOS | <cache dir>/validator/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator |
| Windows | <cache dir>\validator\OrcaSlicer_profile_validator.exe |
Copying the cached profile-fixtures/ into the new dir reuses the fixture archives; the fixture
manifest.json is still downloaded on every run, so validate_custom needs the network either way.
another run is using <dir> means a live run holds <dir>/.lock: leave it and use your own
--work-dir. Only a .lock with no check_profile process alive is a crash leftover; delete it by
hand.
The five checks
| Check | Command it runs | Catches |
|---|---|---|
profile_tool |
python3 scripts/orca_profile_tool.py check |
index coverage both ways, preset-name collisions, files normalize / update-index would still rewrite, duplicate JSON keys, filament compatible_printers, filament_type array, conflict keys, variant strings and array widths and layout keys, dangling default_materials, id length, all setting_id and filament_id rules (below) |
validate_system |
validator -p resources/profiles -l 2 |
load errors, missing filament compatible_printers, dangling inherits / compatible_*, duplicate filament_id per printer (Ambiguous AMS filament match), printer defaults that name no compatible system filament |
validate_slice |
validator -p … -s -l 2 |
custom G-code expansion and unresolvable printer defaults, by slicing |
validate_filament_subtypes |
validator -p … -l 2 -f |
nothing extra; see below |
validate_custom |
validator -p <tree + fixture> -l 2 |
a shipped preset name that a past release offered no longer resolving |
-f is a no-op. It defaults to on, so the duplicate-filament_id check runs whether or not you
pass it, and validate_system already fails on duplicates. The binary's own --help ("Off unless this
flag is present") does not reflect that default.
A --vendor run reads differently from CI. The validator's -v loads that vendor plus
OrcaFilamentLibrary and nothing else, so every library tune whose compatible_printers names another
vendor's printers fails validate_system, validate_filament_subtypes and each validate_custom
fixture with thousands of references unknown compatible_printers "Bambu Lab …" lines. Under
--vendor, profile_tool and validate_slice are the meaningful results; for the other three, filter
the log for your vendor's files and treat only those lines as findings. The unscoped run is the CI
result; run it before the PR.
validate_custom: the backward-compatibility gate
It downloads one fixture archive per past release (v1.9.0 onwards) of generated mock user presets: a
<vendor>_<preset>_orca_test copy of every system preset that release shipped, cut with the
validator's own -g 1 mode. It unpacks each over a copy of the current tree and loads it. Each entry
holds only inherits plus a canned diff, so the one failure it adds over validate_system is a shipped
preset name disappearing. This is what makes a rename, a deletion or an instantiation flip a CI
failure rather than just a user complaint, and the reason renamed_from is mandatory.
The whole current tree sits under each fixture, so every validate_system error fails
validate_custom too: fix validate_system first. Under --vendor it copies only the top-level index
files, <Vendor>/ and OrcaFilamentLibrary/, and picks fixtures by the index's display name
(Bambulab for BBL), not the file stem; fixture presets without that prefix are covered only by an
unscoped run, and it warns validate_custom checked nothing when none match.
validate_slice
It slices a two-colour cube on every instantiable printer in the tree, sequentially, forcing the prime
tower. It selects default_print_profile and the first default_filament_profile, then updates
compatibility; that update can select a different compatible preset. Confirm the intended defaults
yourself rather than treating a passing sweep as proof that those exact presets were sliced.
A printer fails if it cannot be selected, falls back to a Default preset, throws, produces no G-code,
or emits no CP TOOLCHANGE START (change_filament_gcode never expanded). Non-default processes and
filaments get no dedicated coverage; slice them on a copy. In the
default set, a bundle without a machine/ folder is recorded as SKIP; naming validate_slice
explicitly for it fails (No instantiable printer presets found for vendor OrcaFilamentLibrary). The
validator logs [error] lines that do not fail a check (such as could not found extruder_type); only
each check's PASS or FAIL counts.
orca_profile_tool.py check
check is one subcommand of the tool that also owns fix-variant, generate-id, normalize,
trim and update-index; ids.md has the writing half.
| Catches | Scope | Function in the tool |
|---|---|---|
| two files in one bundle claiming one type + name, indexed or not | per vendor | check_preset_name_uniqueness |
a file on disk that no *_list references (an error, not a warning) |
per vendor | check_index_coverage |
an index entry whose name disagrees with the file, or whose sub_path is missing |
per vendor | check_name_consistency |
a file normalize would rewrite, an index update-index would rebuild |
per vendor | check_normalized |
| duplicate JSON keys in a file | every file read | the JSON loader |
an instantiated non-library filament with no non-empty compatible_printers of its own |
per vendor | check_filament_compatible_printers |
extruder_clearance_radius alongside extruder_clearance_max_radius |
per vendor | check_conflict_keys |
a scalar filament_type ("filament_type": "PLA"); the five other filament vectors normalize arrayifies surface as normalize would convert <field> to an array |
per vendor | check_vector_type_keys, check_normalized |
a variant string the two enums cannot build (a dead variant, a legacy spelling included), a variant list naming one variant twice, an extruder_type, nozzle_volume_type or default_nozzle_volume_type that is not an enum name (variant names) |
per vendor | check_variant_names |
a variant array not exactly variant length × stride wide in a selectable preset that writes it (with --strict, also in one it reaches); machine variant layout keys that disagree; a process id array that does not pair each variant (variant arrays) |
per vendor | check_variant_arrays |
a declared filament_id longer than 8 characters |
per vendor | check_filament_id_length |
a default_materials name, or a default_filament_profile name, matching no filament file (below) |
per vendor | check_machine_default_materials |
| per-key warnings for ignored options, filament files only | per vendor | check_obsolete_keys |
setting_id uniqueness, every filament_id rule, and machine_model names duplicated across bundles |
tree-wide, ignoring --vendor entirely |
check_setting_id_uniqueness, check_filament_ids, check_machine_model_name_uniqueness |
Because the id and model-name checks stay tree-wide, a vendor-scoped run can and does fail on another vendor's files, and it saves seconds, not minutes.
Unscoped, the per-vendor pass covers every bundle. The only exclusion is the stray user/ directory
(below); OrcaFilamentLibrary is held to the same rules as any vendor, its sole exemption being that a
library filament may leave compatible_printers empty. check_normalized covers every bundle with an
index.
Notes that matter:
- Exit codes: 0 clean, 1 errors found, 2 argparse misuse. Warnings never change the exit code.
- A nonexistent
--vendoris a hard error:[ERROR] unknown vendor "<V>" in <dir>, exit 1. --vendor ""means all vendors;check_profile.shrelies on that.--vendoris repeatable (check --vendor A --vendor OrcaFilamentLibrary);check_profile.shtakes one.- A stray directory under
resources/profiles/still gets counted as a vendor by the per-vendor pass and warned about (No profiles found for vendor: <dir> at …/<dir>.json, and the "Checked vendors" count goes up by one): usually an emptied folder, or auser/left by a direct validator run. An unscopedcheckskipsuser/by name;--vendor userstill checks and warns about it.normalize,trimandupdate-indexignore strays too: they define a bundle as a directory with a matching index file. - Each remedy is printed once for the whole run, not once per file, as a
[WARNING]under the errors (2 unreferenced file(s) above: delete them, or run … update-index). Read those lines: they name the command that fixes the batch. - When there are errors or warnings, the trailing summary suggests
normalize. That is right for the shape errors and misleading for everything else: an id error needsgenerate-id, a danglingdefault_materialsneeds a human. - Other options:
--dry-runon every writing command,--profiles DIRto point any command at another tree,--profile-typeto narrownormalize,trimandupdate-indexto one type (ids.md). resources/profiles/check_unused_setting_id.pyis a legacy BBL-only diagnostic, not part of profile CI. Useorca_profile_tool.py checkfor current id validation.
Obsolete keys
check always reports per-key warnings for obsolete options in filament profiles. Its normalization
check also rejects obsolete keys across all preset types (normalize would remove <key>); normalize
removes them.
Default-material references
The materials check finds default_materials / default_filament_profile entries naming a preset that
does not exist. It reads each machine/ file's own key (a model's default_materials, a variant's
default_filament_profile; a file that writes both is checked on default_materials only) and accepts
any name found in the vendor's or OrcaFilamentLibrary's filament/ files, bases and unindexed files
included. The three authoring errors it surfaces are , instead of ;, wrong case (@system), and a
whole ;-joined string stuffed into one array element. Only the validator (validate_system) requires
an instantiated system filament that is compatible with each variant.
Variant arrays
check_variant_arrays composes every selectable preset the loader's way (the parent, then each
include in order, then the file's own keys; a filament's inherits may fall through to
OrcaFilamentLibrary) and holds each key of the four variant sets
that the preset writes itself to exactly its variant length × stride
(widths). The variant length is the length of the composed preset's
own *_extruder_variant list; without one, a machine's is the number of variants its
extruder_variant_list offers, else its extruder count (the list's default is one
Direct Drive Standard per extruder), and a process's or filament's is 1. Any other width is an
error, one value included and even when every value is the same. A key the preset does not write
takes what reaches it, the default or an array it inherits or includes, which the loader resizes; it
is not checked. A base is not judged on its own: what it writes counts only where it reaches a preset
that does not override it.
check --strict also holds every selectable preset to its own width for each key that reaches it,
so a preset whose variants differ from those of the file its array comes from restates the array
(the error names that file). That is BBL's practice and the target for new printer-specific presets;
CI runs check without --strict.
An id array that reaches a selectable preset, printer_extruder_id or print_extruder_id, written or
inherited, must have one entry per entry of its variant list on any printer. This rule and the
machine layout rules below judge the composed preset without --strict, whichever file writes the
keys. Beside a written variant list this rule reports it instead of the width
rule, so it is reported once. A process that
lists variants without print_extruder_id gets a warning when a variant repeats (every entry then
reads as extruder 1, so the repeated variant is unreachable), nothing otherwise.
On a machine the layout keys are held to Printer rules 1–4, every failure an error unless marked:
- With
extruder_variant_listwritten: the list has one entry per extruder, as many asnozzle_diameter; every variant starts with its extruder'sextruder_type;default_nozzle_volume_typenames a nozzle volume type that extruder lists;printer_extruder_variantis the list flattened extruder-major andprinter_extruder_idgives each entry its 1-based extruder (an id array left out reads as extruder 1 everywhere, which passes when those are the flattening's ids). Without the pair, the list may offer one variant in total. - With the pair written and no
extruder_variant_list: one variant per extruder at most. A pair thatsingle_extruder_multi_materialoff would replace with the default at load is a warning; a pair the rebuild would leave as it is passes.
It does not see a base's own resize: it composes at the width each file wrote, so an array wider
than a base's list, which the loader cuts before any child inherits it, passes even with --strict
(composition). Nor does it see per-extruder
vectors outside the sets (extruder_offset, printer_extruder_options, …), which no variant list
sizes. What a variant holds (a High Flow variant copied from Standard, a variant inserted at the wrong
index) is review work
(item 14);
whether it is a name the engine can select at all is check_variant_names'.
python3 scripts/orca_profile_tool.py fix-variant resizes every array the width rule rejects in the
selectable preset that writes it, leaves bases alone and adds no key: extra values are dropped, missing ones
repeat the last value (the last normal/silent pair at stride 2; a lone value fills normal and silent
alike). It leaves the variant lists and id arrays to you, since they address the variants rather than
fill them. fix-variant --strict then also writes each key that reaches a selectable preset at
another width: into the most general file on the way down to the preset whose own width is the
preset's and whose selectable presets taking it all need that width, else into the preset itself.
Presets of every bundle count towards that agreement; --vendor limits the files written and
--dry-run previews. The loader pads with the first value where fix-variant repeats the last, so a
padded array need not load as before, and
trimming deletes values: when the extra values were meant as per-extruder or per-variant values,
declare the variant layout instead (Printer rule 2) and keep
them. fix-variant moves no value, so a family whose presets all wrote one value repeats the widened
array in each; put it on the family's base afterwards (shared bases).
Variant names
check_variant_names reads the four list keys plus extruder_type, nozzle_volume_type and
default_nozzle_volume_type of every preset the bundle's index references, bases included, and holds
each entry to the names the engine's two enum maps define (s_keys_map_ExtruderType,
s_keys_map_NozzleVolumeType, read from PrintConfig.cpp on every run). The bundle's own files are
judged, not the composed config: a bad string is the writing file's error, once. Every finding is an
error, and no bundle is exempt: BBL, whose bundle is imported from BambuStudio, is held to OrcaSlicer's
enums like any other.
- A variant string outside
<extruder type> <nozzle volume type>is a dead variant: it still counts toward the variant length the arrays are sized by, so the values written for it silently never reach the G-code.Hybridtoo, which is runtime-only, and an empty entry. A name BambuStudio's enum has and OrcaSlicer's lacks is dead here as well, and passes with no tool change once the engine gains that nozzle volume type. - A legacy name the loader still rewrites in these keys (
Normal→Standard,Big Traffic→High Flow) is an error that names the enum name to write; the profile has to spell the enum name.DirectDriveis only rewritten inextruder_type, so a variant string carrying it is dead. - A variant list naming one variant twice is an error: the lookup returns the first equal string, so
the repeat is unreachable and its value sits at an index no extruder reads. A filament list takes
strings,
extruder_variant_listtakes them per extruder, and a process takes(extruder id, variant)pairs — one string on two extruders is two pairs, not a repeat. - An
extruder_type,nozzle_volume_typeordefault_nozzle_volume_typevalue that is not an enum name is an error: they are enum options, so an unknown value fails the validator's load of the whole bundle, while the app silently loads the option's default instead. A legacy spelling (DirectDrive,Normal,Big Traffic) andHybrid, an enum value no profile writes, are errors too.
The variant order, the choice of variants, and the values themselves are not checked.
normalize and update-index are part of the check
check fails when either command would still change something, so they are not optional polish: the
file that gets reviewed has to be the file that ships. What normalize changes is narrow and fixed:
- adds a missing
type; - deletes a
versionoris_custom_definedkey from a preset file; - deletes six print-speed keys from filament profiles (
initial_layer_print_speed,outer_wall_speed,inner_wall_speed,infill_speed,top_surface_speed,travel_speed); - deletes the obsolete keys the loader ignores (the
ignoreset inPrintConfigDef::handle_legacy), across preset types; - resolves the
extruder_clearance_*conflict pair by keeping the larger; - arrayifies six filament options (
filament_type,filament_cost,filament_density,temperature_vitrification,filament_max_volumetric_speed,filament_vendor); - hoists
type,name,renamed_from,inherits,from,setting_id,filament_id,instantiationto the front.
A file it changes is then rewritten whole: tab-indented, LF, one trailing newline, keys reordered. A file committed with CRLF line endings therefore changes on every line; read the diff before committing it.
Set type explicitly when authoring. For a file in machine/ without it, normalization guesses
machine only if its name contains nozzle (case-insensitive), otherwise machine_model. That
heuristic cannot reliably classify shared machine bases or unusually named variants.
The tool's obsolete-key set is checked against the loader's ignore list by a unit test. Active options
are preserved, including live keys whose values the loader rewrites (extruder_type: DirectDrive →
Direct Drive; the variant-string keys: Normal / Big Traffic → Standard / High Flow), and so are
legacy key names the loader migrates (such as extruder_clearance_max_radius).
Two things it therefore does not enforce:
- Formatting and key order on their own. A file with none of those problems is skipped entirely, so
4-space indent, a missing trailing newline, and a file that leads with
compatible_printersall passcheck. They stay latent until something else tripsnormalizeand the whole file reformats inside an unrelated diff. (normalize --forcerewrites every file; do not run it on a shipped bundle.) - A misspelled setting key.
inital_layer_heightandsparse_infill_densitipasscheckcleanly. Verify new keys againstsrc/libslic3r/PrintConfig.cppand the loader's legacy handling (PrintConfigDef::handle_legacy: renamed keys, rewritten values and ignored keys).
check does not catch a dangling default_print_profile either; check that name by hand.
The validator binary
Built from src/dev-utils/OrcaSlicer_profile_validator.cpp with -DORCA_TOOLS=ON. Both scripts use a
local build under build*/ when one exists, else they download the nightly into the validator
subdirectory of the cache dir. check_profile.sh searches Release, then RelWithDebInfo, then Debug
(each under build*/src/<config> and build*/*/src/<config>), then single-config build*/src, and
prefers a host-architecture build tree; check_profile.ps1 tries Release, RelWithDebInfo, MinSizeRel,
then Debug. A stale local build is used silently; --download / -Download skips local builds and uses the
nightly. The download and the fixtures stay cached until --refresh / -Refresh, so
--download --refresh (-Download -Refresh) matches CI exactly. Windows looks for
OrcaSlicer_profile_validator.exe.
If your build lives somewhere else, point at it with --validator / -Validator, or set
ORCA_PROFILE_VALIDATOR ($env:ORCA_PROFILE_VALIDATOR in PowerShell).
| Flag | Meaning |
|---|---|
-p <dir> |
profile tree (also becomes the data dir) |
-l <n> |
log level; CI uses 2 |
-v <Vendor> |
load only that vendor plus OrcaFilamentLibrary |
-s |
slice sweep |
-o <dir> |
with -s, save each printer's G-code there |
-f |
no-op (see above) |
-g 1 |
regenerate user-preset fixtures; takes a value, and wipes the user preset dir first |
On ARM64 Linux the nightly is x86-64 only: the script warns and downloads anyway, producing a binary
that will not run. Build it locally with -DORCA_TOOLS=ON and pass --validator.
Running the validator directly uses the profile tree as its data directory and can create user/
there. Prefer the wrappers, which stash existing user presets and restore them afterward. After a
direct run, inspect user/ and remove only empty directories created by that run; fixtures or
pre-existing user files may be present.
Checking a copy of the tree
Use --profiles DIR on the Python tool and -p DIR on the validator. The wrappers' --profiles /
-ProfilesDir passes the tree to both, so one run validates a copy fully:
./scripts/check_profile.sh --profiles "<tree>"
# Windows: scripts\check_profile.bat -ProfilesDir "<tree>"
To slice a process or filament that is not a printer's default, or to read the G-code, work on a copy:
copy resources/profiles and resources/info into one scratch dir (the validator reads info/ next to
the tree), point the printer's default_print_profile or first default_filament_profile at the preset
in the copy, and run the validator with -o:
<validator> -p <scratch>/profiles -v "<Vendor>" -s -o <gcode dir>
Each printer's G-code is saved as <vendor name>__<printer>.gcode; its embedded config
(; print_settings_id = …, ; filament_settings_id = …) shows what was actually sliced.
Testing in the app
Editing this checkout's resources/profiles does not update a separately installed application. Test
with a build using the edited resources and a bumped bundle version: the updater installs newer bundles
under <data_dir>/system/, and the preset cache also depends on the bundle version. Use Help ▸ Show
Configuration Folder to locate the active data directory:
| Platform | Default data directory |
|---|---|
| macOS | ~/Library/Application Support/OrcaSlicer |
| Linux | $XDG_CONFIG_HOME/OrcaSlicer, or ~/.config/OrcaSlicer when unset |
| Windows | %APPDATA%\OrcaSlicer |
A portable data_dir next to the executable takes precedence. Use a separate test configuration for a
clean-install check; preserve the normal configuration and user presets.
Error → remedy
| Message | Fix |
|---|---|
can not find inherits <parent> for <preset> |
parent missing, unregistered, misspelled, or listed after the child |
can not find include |
the template is misspelled, registered after the includer, or selectable |
can not find filament_id for <name> |
nothing in the chain declares one: run generate-id |
can not find parent <name> for config <user preset>! |
a shipped name disappeared: add renamed_from |
Failed loading configuration file <file> |
that file could not be loaded and the whole bundle was discarded: a JSON error, or a value its option cannot take, such as nil in a non-nullable key (Invalid value provided for parameter <key>: nil, Deserializing nil into a non-nullable object); the lines above it name the cause |
Missing instantiation attribute for <name> |
key absent or not the string "true" / "false" |
contains incorrect keys: <keys>, which were removed |
a key valid for a different preset type |
defines invalid printer variant "<v>" |
not a token of the model's nozzle_diameter list |
has printer_variant "<v>" that does not match its nozzle_diameter |
the set comparison |
references unknown compatible_printers "<p>" |
the printer was renamed or deleted, or a machine_model name was used instead of a variant name: fix the reference. Under --vendor, usually another vendor's printer (why) |
references renamed compatible_printers "<old>" (now "<new>") |
in-tree references must name the current preset; renamed_from does not excuse them |
Filament preset "<f>" is missing compatible_printers setting |
non-library filaments need a non-empty list in their own file; the resolved-vs-own-key trap is in filament-profiles.md |
Ambiguous AMS filament match: N filament presets share filament_id "X" and are all compatible with printer "Y" |
make the lists disjoint by specificity: the specialized profile keeps the variant, the general ones drop it; prefer this over deleting a profile. Or fix an inherits pointing at another product's @base. orca_profile_tool.py check does not catch this; only validate_system does |
Layer height cannot exceed nozzle diameter. / Line width too small / Line width too large |
the slicing limits |
[ERROR] … no <V>.json list references it, so it never loads |
update-index, or delete the file |
[ERROR] … no <V>.json list references it and it declares no profile type |
set the correct type explicitly, then normalize and update-index |
[ERROR] … normalize would <change> / <V>.json: update-index would rebuild <lists> |
run that command and commit the result |
[ERROR] <V> has N <type> profiles named "<name>" |
identify the intended preset and remove or rename the duplicate; use trim --dry-run only for deliberate unindexed-file cleanup |
Duplicate key error in <file>: Duplicate key detected: <key> |
a key written twice in one file; keep the intended one |
… must not have a setting_id / … is missing a setting_id / setting_id "X" in <file> does not match the expected "Y" … |
generate-id --setting-id (by hand in BBL/, see ids.md) |
filament_id "X" declared by … does not match the mint of its triple … |
generate-id --filament-id; if the id should not be declared here at all, remove it so the preset inherits its root's |
inherits filament_id "X" but its own triple "V/T/N" mints "Y" |
generate-id will not fix this; see ids.md |
"<key>" has N values for variant length S at stride k, which takes M (with (no <list key>, so …) after S, naming where S came from, when the preset writes no list, and (it comes from <file>) at the end under --strict for an array the preset does not write) |
exactly S × k values in variant order, or leave the key out (widths); on a list-less multi-extruder printer whose extruders differ, declare the layout; fix-variant cuts or pads to that width (variant arrays); an S you did not expect means the variant list did not resolve through include or inherits |
printer_extruder_variant […] is not extruder_variant_list flattened extruder-major […] / printer_extruder_id […] does not give each entry of printer_extruder_variant its 1-based extruder […] / printer_extruder_id has N entries for the M entries of printer_extruder_variant |
write the pair as the flattening of the list, ids in step (Printer rule 2) |
extruder_variant_list has N entries for M extruder(s) / extruder_variant_list entry i "…" holds a variant that does not start with extruder i's extruder_type / default_nozzle_volume_type "…" is not a nozzle volume type extruder i lists |
Printer rules 1, 3 and 4 |
extruder_variant_list offers N variants but the preset writes no printer_extruder_variant/printer_extruder_id / printer_extruder_variant lists several variants for extruder N but the preset writes no extruder_variant_list / [WARNING] … with single_extruder_multi_material off the loader replaces printer_extruder_variant … |
write all three layout keys (Printer rule 2) |
print_extruder_id has N entries for the M entries of print_extruder_variant / [WARNING] … print_extruder_variant repeats a variant but print_extruder_id is absent |
one id per variant entry, mirroring the printer's pairs (Process rule 1) |
<key> <where> holds "…", which no extruder can select: "…" is not a nozzle volume type the enum has (…) / … it does not start with an extruder type the enum has (…) / … is empty / … Hybrid names the sub-nozzles of one hybrid extruder at runtime |
write a legal variant string, <extruder type> <nozzle volume type> from the two enums (variant names, variant strings); in every bundle, BBL included |
… holds the legacy variant "…" / extruder_type spells the legacy name "…" / nozzle_volume_type spells the legacy name "…" / default_nozzle_volume_type spells the legacy name "…" |
write the enum name the message gives: the loader still rewrites the legacy one, but check rejects it |
<key> lists "…" N times / print_extruder_variant lists the pair (extruder N, "…") N times |
drop the repeat and its value from every variant array: the lookup returns the first equal string |
extruder_type "…" is not one of (…) / nozzle_volume_type "…" is not one of (…) / default_nozzle_volume_type "…" is not one of (…) / … names "Hybrid", which the engine computes for a hybrid extruder at runtime |
they are enum options, so an unknown value fails the validator's load of the whole bundle and silently becomes the default in the app; use s_keys_map_ExtruderType / s_keys_map_NozzleVolumeType, Hybrid excepted (variant strings) |
[WARNING] No profiles found for vendor: <dir> |
a directory with no matching index (an emptied folder, or a user/ left by a direct validator run); remove it |
… has no compatible system filament in its model's "default_materials" |
add a system filament preset compatible with that variant to the model's default_materials |
… names the unknown system filament "<n>" in its "default_materials" / … "default_filament_profile" |
name an existing system (not user, not base) filament exactly; ; separators, exact case |
Missing filament profile: '<n>' referenced in <file> |
the same, caught by check; usual causes are , instead of ;, wrong case (@system), or a ;-joined list packed into one array element |
machine_model name "<n>" is declared by N bundles |
model names are unique across the tree; rename the new model |
vendor <V>'s config version: <s> invalid |
the version string does not parse; write MM.mm.pp.bb |
[json.exception.type_error.302] type must be string |
locate the non-string value in the index or a model; see failure scopes |
Printer "<p>" fell back to a default preset |
the final process or filament selection is a generic Default preset: check the named defaults, their visibility and that compatible presets exist. An incompatible default may instead be replaced without this error |
Printer "<p>" sliced but the filament change never fired (no CP TOOLCHANGE START) |
change_filament_gcode never expanded |
CI
.github/workflows/check_profiles.yml, job "Check profiles", runs on pull_request into main or
release/* touching resources/profiles/**, resources/printers/**, scripts/**,
src/libslic3r/PrintConfig.cpp (where the tool reads the variant key sets) or the workflow itself.
There is no push trigger: a direct push to main runs no profile validation.
The job opens with python3 -m unittest discover -s scripts/tests -t scripts, the tool's own unit tests
(run them locally after changing scripts/, with py -3 on Windows, and keep -t scripts or the
imports fail). That step is deliberately not continue-on-error: a broken tool makes everything it
then says about the profiles worthless. Every check after it is continue-on-error with a final gate,
so one run reports all five results. On failure a second workflow posts or replaces a single PR comment
marked <!-- profile-validation-comment -->, holding the start of each failing log (30 KB per check,
12 KB per validate_custom fixture; reproduce locally for the full list); it deletes the comment once
the run is green.
The job name is also the required check for the delegated-merge bot, which lets a vendor maintainer
self-merge a PR limited to their own resources/profiles/<Vendor>/ folder with no human review, so
whatever CI does not check is what ships unreviewed. Its denied patterns refuse ^scripts/ and any
.py, so a PR that touches the tooling always needs a maintainer.