filament_id is the plain mint of the product triple (filament_vendor, filament_type, filament name), and nothing else feeds it. The tooling used to accept any salt iteration of a preset's own triple, and its minting policy stepped past ids that other products held in the tree or in the snapshot, so which id a product got could depend on history. Every "salt split" in the tree masked a redundant preset rather than a real need, and no shipped id is salted, so salting goes entirely: no salt parameter, no id policy object, and --generate no longer reads the snapshot. --check now holds every declared and every inherited id to that one value and lists each preset that misses it, variants under a wrong root included, instead of folding them into the root's error. Two products whose triples mint one id is reported as a collision naming both, and --generate refuses to write it; the remedy is a rename so the triples differ. The Bambu catalog map generator keys its rows by the same mint rather than by what the tree already ships. No id changes: all 913 ids in the tree are already the mint of their triple, so --generate is a no-op and the snapshot is untouched.
35 KiB
Filament IDs (filament_id)
filament_id identifies one filament product: one named spool product = one id, shared by
all of that product's per-printer / per-nozzle variants, in every profile bundle that ships it.
Devices use it to match a physical spool or tray to a filament preset. It is never per-color,
per-printer, per-nozzle, or per-preset (per-preset identity is setting_id), and it is never
per-bundle either — PolyLite PLA carries the same id whether the preset lives in the
OrcaFilamentLibrary (OFL), Qidi, or Snapmaker bundle. The granularity is the name on the spool,
not the brand behind it: AAA PLA Lite and AAA PLA Pro are two filaments with two ids, not
variants of one.
How it is generated: an id is computed, never invented. scripts/orca_id_tool.py
mints it as a deterministic hash of the product's identity — the triple
(filament_vendor, filament_type, filament name), where the filament name is the preset name
with its @... variant suffix stripped — producing an 8-character OF* code that is the
same for that product in every bundle, in every PR, on every machine. For example, Polymaker's
PolyLite PLA presets (PolyLite PLA @base, PolyLite PLA@Q2-Series, …) resolve
filament_vendor Polymaker, filament_type PLA, and filament name PolyLite PLA; hashing
filament_product/Polymaker/PLA/PolyLite PLA yields OF5CgdDq, and that is the id the
OrcaFilamentLibrary, OrcaArena, Qidi, and Snapmaker bundles all arrive at independently
(derivation details in the Minting section).
How it is used: at runtime the id is the join key between hardware and profiles. When a printer reports what a tray holds (Bambu AMS, Qidi box, Creality CFS, Klipper, Snapmaker), OrcaSlicer matches the reported id against the filament presets compatible with that printer to select the right profile; other features — tray display names, support-material detection, vitrification warnings, multi-nozzle filament grouping — look up material properties by id alone. An id that changes is not forwarded anywhere: a tray or record still holding the old value falls back to matching by material type until the user re-selects the filament, so identity changes are made deliberately and rarely.
This page is the rule for authoring filament_id in system profiles
(resources/profiles/**). CI enforces everything below; the short version is:
Important
Never write a
filament_idvalue by hand. A new filament gets its id frompython scripts/orca_id_tool.py --generate; one already in the tree has one — inherit it.
The design, in two pieces
Because several consumers match globally by id alone, first hit wins (see the next section), any two materials sharing one id feed wrong data somewhere — a wrong tray name, a wrong support-material flag, a wrong nozzle grouping — and inside one printer a duplicated id makes AMS spool matching a coin toss. Hand-written ids produce such collisions constantly, so the system is built to make them impossible:
- Deterministic minting. An id is a pure hash of the product's identity — no registry to maintain, no next-free-number ceremony, no way for two concurrent PRs to race for the same number, and no way to get it wrong by hand, because you never write it by hand.
- A sanctioned snapshot. The complete id landscape derived from the tree must equal
scripts/filament_id_snapshot.jsonexactly, so every change to ids, claims (which bundles ship which id, and for which filament), or product identity surfaces as a reviewable diff to one file — the maintainer gate.
Who consumes the id
The canonical consumer is tray-to-preset matching: a device reports a tray material id
(tray_info_idx), and the shared matching pipeline (PresetBundle::sync_ams_list and
friends) resolves it to a preset. The matcher is printer-scoped and first-match-wins:
scanning only compatible root presets — system roots plus user-made custom filaments, which
are user roots carrying their own P* ids; a preset derived from another resolves through
its root and never matches directly — it picks the first one whose filament_id equals the
tray's. On a miss it falls back by filament type: a system Generic <type> preset
(matched by name, then by type similarity), else the slot's previous selection, else any compatible system generic or,
failing that, any compatible system preset, else the slot is skipped — every fallback
selection surfaces a user-visible notice.
Today only the Bambu AMS integration follows this pattern end to end — the device itself
reports the id, BBLPrinterAgent translates it out of Bambu's catalog into ours, and the
pipeline does all the matching. The other device integrations still synthesize a preset id
client-side in their agents (by type, brand, or color lookups against the loaded presets)
before the pipeline runs; they are intended to converge on the same pattern, with the
device-reported tray material id flowing through the shared matcher.
| Ecosystem | Where the tray id comes from today |
|---|---|
| Bambu AMS | the device itself (RFID / user tray setting), in Bambu's own GF* catalog; BBLPrinterAgent rewrites it into our id before the matcher sees it (see The Bambu catalog map) |
| Qidi box | composed at runtime as QD_<series>_<vendor>_<typeidx> — vendor and type indices from the device's per-slot saved variables, the series digit inferred client-side from the printer model/name. No preset carries a QD_* value, so the slot currently resolves by filament type; mapping the composed id onto the filament's minted id belongs in the agent |
| Creality CFS | runtime brand/type scoring returns the winning preset's id |
| Klipper (AFC / Happy Hare) | runtime lookup by filament type |
| Snapmaker | runtime color/vendor/type match |
Tray-to-preset matching is printer-scoped, but several consumers match globally by id alone,
first hit wins: tray display names, filament_is_support, vitrification warnings, and
multi-nozzle filament grouping in the slicing pipeline (FilamentGroup::try_merge_filaments
merges plate slots sharing one (filament_id, color) pair, with matching
extruder-printability, onto one nozzle group; the engine is implemented but no grouping path
calls it yet).
Two different materials sharing one id
feed wrong data to those consumers even when the presets live in different vendors — so
cross-material id sharing is never safe. Within one printer, duplicate ids break AMS matching:
the matcher picks whichever preset loads first (it now logs an "Ambiguous AMS filament match"
warning, but the pick is still arbitrary) and the tray-edit dialog, which lists one entry per
id, hides the second preset entirely. The profile validator's -f check
(check_filament_subtypes → PresetBundle::check_duplicate_filament_subtypes) rejects this
per printer, and CI runs it tree-wide.
Two more consumer-side facts worth knowing:
- The machine-facing dialogs (AMS tray edit, AMS dry control, calibration history, extrusion
calibration) offer the filaments a connected printer can use by the same compatibility rule
the plater uses (an empty
compatible_printersmeans every printer). Alias shadowing still applies: a vendor's same-name profile supersedes the library generic. That is what puts Orca Filament Library materials in those lists — deduplicated to one entry perfilament_idin the AMS and calibration-history dialogs, while extrusion calibration deliberately lists every matching preset by full name. - The id is load-bearing at startup: an instantiated system filament (one marked
"instantiation": "true"— see the structure rules) that resolves nofilament_idanywhere in itsinheritschain is a hard load error in the C++ loader (Can not find filament_id for <name>) that discards the entire vendor bundle (for the OrcaFilamentLibrary itself the failure is messier: library presets loaded before the failing one survive, and every vendor bundle whose filaments inherit from the library is then discarded for want of a base). CI's structure check catches this before it ships.
Do I need a new id? The one-question test
Would a user consider this a different spool product than anything already in the tree?
Different polymer, different sub-brand (Basic / Matte / Silk / HF), fiber-filled sibling, or a
second selectable diameter → new filament, new id. The same spool tuned for another printer
or nozzle → join the existing filament (keep its base name and inherit it; no id
key needed). Tuning a generic material → join the OrcaFilamentLibrary filament (inherit
Generic X @System and keep the Generic X base name; no id key needed).
| Situation | id |
|---|---|
| Per-printer / per-nozzle variant of an existing material | same id (inherit it) |
| Sub-brand or product line (PLA vs PLA Matte vs PLA Silk vs PLA HF) | new id each |
| Color | never a new id |
| Second diameter of the same product (1.75 + 2.85) | sibling filament, new id |
| "High-speed" tuned for a different printer model | same id (it is a printer variant) |
| "High-speed" selectable alongside the normal preset on one printer | new name, so a new id (it is a product line) |
Structure rules
- Every preset carries the id of its own product, wherever it gets it from. The id is a
function of the preset's own triple (rule 5), and
inheritscarries settings, never identity. So a preset may declare the key itself or inherit it from any ancestor — a<Filament> @baseroot, a real (instantiated) preset of the same filament, an OrcaFilamentLibrary preset — and CI checks one thing: the id it ends up with equals the mint of its triple. The usual shape is one@baseroot ("instantiation": "false") declaring the key and the per-printer variants inheriting it; a filament may have several roots — Qidi's PolyLite PLA has four per-series roots (PolyLite PLA@Q2-Series,@Q2C-Series,@X-Max 4-Series,@X-Plus 5-Series) — which then all declare the identical id. A branded filament that borrows a generic's settings (Flashforge ABS Basic @FF C5inheritsGeneric ABS @System) declares its own id, because its triple is its own. - The filament name is the base name: the preset name with everything from the first
(optionally space-preceded)
@stripped.MyBrand PLA @Orca 3D Fuse1andMyBrand PLA@HSare both the filamentMyBrand PLA. - Within one filament, variants'
compatible_printersare pairwise disjoint — per printer, at most one compatible instantiated preset per id, or AMS matching turns ambiguous. The C++ validator's-fcheck enforces this, tree-wide in CI. Since one product carries one id and cannot be split onto two, this rule is the only remedy for such an ambiguity: narrow thecompatible_printers, or retire the preset that duplicates another. - Generics belong to OrcaFilamentLibrary. A vendor tuning a generic material inherits
Generic X @System, keeps theGeneric Xbase name (that alias is what hides the library preset on your printers, and it is what makes its triple — and so its id — the library's) and sets a non-emptycompatible_printers— e.g.Generic PLA @Sovol SV08 MAXinheritsGeneric PLA @Systemand lists three Sovol nozzles. Renaming such a preset makes it a different product by rule 5, so it then needs its own id. - Ids follow the product identity. The id is a pure function of the product triple
(filament_vendor, filament_type, filament name), so correcting any of them re-mints the id by design, applied by--generate(preview with--dry-run, confine with--vendor) and gated by the--update-snapshotdiff; the exact sequence is in the FAQ. Nothing forwards the old value, so anything outside the tree that stored it — a device tray, a calibration record, a saved project — falls back to matching by filament type until the user re-selects the filament. Re-mint deliberately, and only to fix a genuinely wrong identity. (renamed_fromstill gates preset-name compatibility, as before.)
Minting — nobody invents ids
New ids are deterministic, computed exactly like the setting_id precedent
(the setting_id half of scripts/orca_id_tool.py):
FILAMENT_ID_NAMESPACE = uuid5(setting-id NAMESPACE, "filament_id")
= c4d3ff49-4c32-5534-a3e3-00894157ab97
filament_id = "OF" + base62_6( uuid5(FILAMENT_ID_NAMESPACE,
"filament_product/<filament_vendor>/<filament_type>/<filament_name>") )
base62_6 is the low 6 base62 digits (alphabet 0-9A-Za-z) of the UUID taken as a big-endian
integer, most-significant digit first; with the OF prefix the full id is 8 chars, within the
AMS length limit. The triple comes from the root preset's flattened config:
<filament_vendor> is the filament
manufacturer ("Polymaker", or "Generic" for generics — never the printer brand),
<filament_type> the material type, <filament_name> the root's base name; the two config
values are inheritable list options and the first element counts.
Content-addressing on that triple is what makes the whole system converge. The key contains no
bundle name, so the same product mints the same id in every bundle — moving a filament into
OrcaFilamentLibrary never changes its id, and two vendors independently shipping the same
product arrive at the same id without coordinating. Polymaker/PLA/PolyLite PLA mints
OF5CgdDq, and that one id is declared by the OrcaFilamentLibrary, OrcaArena, Qidi, and
Snapmaker bundles alike; the OFL generic Generic/PLA/Generic PLA mints OFDSrzZ8, claimed
by 35 bundles — most by independent declarations converging on the same mint, the rest
purely through inheritance from the OFL preset.
Nothing but the triple feeds the mint — not the rest of the tree, not the snapshot, not what
another preset of the product happens to carry. Determined triple, determined id: one product
carries one id and there is no second acceptable value for it, so any other value on a preset
is a mismatch --check reports and --generate pulls back. Two different products whose
triples mint the same base62 value would be a collision (a roughly 36-bit id space against a
few thousand products); nothing salts past it: --check reports it naming both products,
--generate refuses to write it, and the remedy is a rename so their triples differ. Where
two presets of one product would be AMS-ambiguous on a printer, the fix is likewise in the
profiles — make their compatible_printers disjoint (structure rule 3), retire the redundant
preset, or, if they really are different products, give them different names so their triples
differ. Never a second id for one triple.
Workflow for a new filament:
# 1. Author the filament with NO filament_id key anywhere.
python scripts/orca_id_tool.py --dry-run # 2. preview the ids — writes nothing
python scripts/orca_id_tool.py --generate # 3. apply them to the profile file(s)
python scripts/orca_id_tool.py --update-snapshot # 4. record the new claims in the snapshot
python scripts/orca_id_tool.py --check # 5. validate the filament_id state
python scripts/orca_extra_profile_check.py # 6. ...and everything else CI checks
# 7. Commit the profile edits together with scripts/filament_id_snapshot.json, for review.
--generate makes every filament's id equal the mint of its own
(filament_vendor, filament_type, filament name) triple: it inserts one where an instantiated
filament resolves none, and re-derives one that does not match. A preset that inherits a
mismatching id is the one case left to the author — check 3b names it, and the fix is to inherit
a preset of the same filament or to give the preset its own key. A declaration is left alone
exactly when it already equals the one id its triple mints, and a collision (check 3d) is
reported and left unwritten. The same run assigns
generate_preset_setting_id(vendor, type, name) to every instantiated filament, process
and machine preset of every vendor except BBL, which keeps its authoritative G* ids, strips
setting_id from base profiles, and fixes the misspelled settings_id key — dropped, or, for
BBL, whose ids have no formula to fall back on, restored under the correct name. It is idempotent and
byte-preserving (indentation, BOM, and line endings intact, every edited file re-parsed to fail
loudly), and a no-op on a tree that already passes scripts/orca_extra_profile_check.py — the
check CI runs over both id kinds, of which --check is the filament_id half.
--filament-idlimits the run tofilament_id.--setting-idlimits the run tosetting_id. The two exclude each other; pass neither to write both.--vendor VENDORconfines the run to that bundle; repeatable. The id is a function of the triple alone, so a narrowed run writes exactly what a full one would;--checkreports whatever it left outside.--dry-runreports what--generatewould do and writes nothing; with no mode of its own it implies--generate, so--dry-run --vendor <Vendor>previews just that bundle.--profiles DIRpoints the tooling at a different profile tree (defaultresources/profiles).--checkand--update-snapshotread and write the sanctioned state of the tree they are given, so pointing them elsewhere needs--snapshot PATHfor that tree too —scripts/filament_id_snapshot.jsondescribesresources/profilesand no other tree.
Identity fixes need no separate mode. --generate re-derives an id that no longer matches its
triple exactly the way it fills in a missing one, so a rename or a filament_vendor /
filament_type correction is just: fix the config, run --generate (confine it with --vendor,
preview it with --dry-run), then --update-snapshot and review the diff.
If you skip the tooling, CI fails and prints the remedy: the expected id for your filament and
the instruction to run python scripts/orca_id_tool.py --generate; once the id is minted, the
snapshot checks likewise point at --update-snapshot and tell you to commit the resulting
diff.
Reserved namespaces — never mint or hand-write into
A reserved namespace is an id space no system profile may declare, because an external catalog or a device protocol owns the values. None of them has an owning vendor: there is no bundle — not even the one whose printers use the catalog — that may write one into a profile.
| Space | Status | Rule |
|---|---|---|
GF* |
Bambu AMS/RFID catalog | declarable by nobody, BBL included: Bambu's own ids live in the generated catalog map, never in a profile |
QD_* |
Qidi device protocol | declarable by nobody, Qidi included: the box composes these ids at runtime and they are not preset ids |
P + 7 hex chars (case-insensitive), "null" |
user-created custom filaments (CreatePresetsDialog.cpp) |
never appears in system profiles |
The two device namespaces, in detail:
- Bambu (
GF*). Bambu's device/RFID/cloud catalog is external and opaque, which is a reason to keep it out of the profiles rather than to let one bundle own it. Every BBL filament mints anOFid from its triple like every other vendor's, and the correspondence to Bambu's catalog ids lives in one generated file the app applies at the printer boundary — the next section. Nothing underresources/profiles/**carries aGF*id today and nothing can be exempted, so aGF*id appearing anywhere in the tree is a mistake, whoever wrote it. - Qidi (
QD_*).QD_*is a device-protocol namespace, not a preset id space: the Qidi box path composesQD_<series>_<vendor>_<typeidx>ids at runtime (slot vendor and type indices reported by the device, the series digit inferred client-side from the printer model/name). Qidi presets carry ordinary mintedOF*ids (generics share the OFL ids), so a composed id matches no preset and the slot falls back to filament type; translating it to the filament's id belongs inQidiPrinterAgent. The alternative — treating per-series protocol ids as preset ids — would put one product under five ids (QIDI PLA Rapidowould beQD_0_1_1throughQD_4_1_1), exactly the fragmentation the mint rule removes.
The Bambu catalog map
Bambu's printers, its AMS and its cloud know only Bambu's own catalog ids. Our profiles carry
minted OF ids like every other vendor's, so one generated file records the correspondence and
the app applies it only where an id crosses to or from a Bambu printer.
The file is resources/printers/bambu_filament_ids.json — a header plus one row per
catalogued product, keyed by our id:
{
"source": "https://github.com/bambulab/BambuStudio",
"bambustudio_commit": "66e405477",
"generated": "2026-09-04",
"filaments": {
"OFhuaUQB": { "bambu_id": "GFB00", "vendor": "Bambu Lab", "type": "ABS", "name": "Bambu ABS" }
}
}
It ships in resources/printers/, next to filaments_blacklist.json — deliberately not in
resources/profiles/, where the loader reads every top-level .json as a vendor index. It
holds 100 rows today, one per product BambuStudio ships, and the correspondence is
one-to-one in both directions.
It is generated, never hand-edited. python scripts/update_bambu_filament_ids.py rebuilds
it from BambuStudio's own shipped BBL bundle — a sparse shallow clone of upstream master,
or --bambustudio-dir <a BambuStudio resources/profiles checkout>. Our BBL bundle is a fork of
Bambu's, tuned and extended independently, so it is not the source of truth for Bambu's ids.
A row's key is the id the product's (filament_vendor, filament_type, filament name) triple
mints — the same id any bundle of ours carries for it, since the id is a function of the triple
alone; the row of a product we do not ship sits inert until some bundle claims that triple —
OFdyfQvU / GFG03, "Bambu PETG Matte", is such a row today.
Regenerate it in the same commit as every BBL profile sync, and read the drift report it prints. Two lines, both informational, neither blocking the write:
upstream ships 'Bambu PETG Matte' (Bambu Lab/PETG), we ship nothing with that identity
Orca BBL filaments with no row: 135 Orca-only product(s)
The first names each upstream product our BBL bundle has no same-identity filament for —
sometimes a genuinely missing product, sometimes a name drift a follow-up rename would
converge. The second counts our own BBL filaments that matched no row: 135 of 234 today, of
which 109 send an OF id on the wire and 26 already rode OF ids inherited from the
OrcaFilamentLibrary. 135 is the number to expect at every regeneration — 109 was the
one-off size of the transition and stopped being computable from the tree once the BBL bundle
was re-minted, so do not "fix" the report to print it.
Check 6 lives in check_filament_ids, so profile CI runs it alongside the other five. It
holds the file to its contract: it parses, carries source / bambustudio_commit /
generated, keys only OF-format ids, maps each Bambu id at most once, and — for every row
whose key the tree actually claims — agrees with the tree on that id's (vendor, type, name)
triple. A row for a product we do not ship is skipped, not an error. The remedy it prints is
always the same: regenerate the map and commit the diff for review.
The runtime rule: swap on hit
Outbound, our id with a row becomes Bambu's; inbound, Bambu's id with a row becomes ours.
Everything else is forwarded untouched — an OF id with no row, a Bambu id for a product we do
not ship, a P-hex user id, "null", an empty string. Translation is confined to the
boundary: nothing between the boundaries ever holds a Bambu id.
Translating one value is a capability of the printer agent: IPrinterAgent declares
to_orca_filament_id and from_orca_filament_id returning their argument, and BBLPrinterAgent
overrides them with Bambu's map, so an agent whose printers already speak our ids inherits the
identity default and translates nothing. NetworkAgent forwards both to the live agent, so the
comparison sites below reach them through wxGetApp().getAgent() and leave an id untranslated
while no agent is live. Whole documents are Bambu's business alone:
BBLPrinterAgent::to_orca_payload and from_orca_payload rewrite every string under
tray_info_idx, filament_id or filamentId at any depth; text that does not parse, or carries
none of those keys, comes back unchanged. The map is loaded once, lazily; a missing or malformed
file degrades to identity with a log line rather than failing.
| Boundary | Where it translates |
|---|---|
| Everything the agent sends | BBLPrinterAgent::send_message and send_message_to_printer, plus PrintParams::ams_mapping_info in dispatch_start — the funnel all five start_* calls share |
| Everything the agent receives | set_on_message_fn and set_on_local_message_fn wrap their callback, so MachineObject::parse_json and everything downstream see our ids only |
| 3mf export | Plater::export_3mf writes Bambu's ids into slice_info.config, gated on preset_bundle.is_bbl_vendor() — the printer reads that file and knows only its own catalog, and no other vendor's export is affected. The CLI has its own writer in OrcaSlicer.cpp; it does the same, gated on the printer_model prefix that already decides Print::is_BBL_printer() for that run |
| Project ingest | Plater::priv::load_files reverse-maps the project's filament_ids before the bundle ingests them, so a project saved by an older Orca or by BambuStudio still resolves the same presets |
| Prints from the printer's SD card | SelectMachineDialog::update_print_required_data reverse-maps each plate's slice-info ids as it adopts the plates, so the AMS mapping dialog pairs them with trays |
| Bambu-specific comparisons | CalibUtils.cpp, DeviceManager.cpp, DeviceCore/DevFilaSystem.cpp, DeviceCore/DevFilaBlackList.cpp, SelectMachine.cpp, AMSDryControl.cpp, AMSMaterialsSetting.cpp, PresetComboBoxes.cpp, ColorDecomposeSupport.cpp |
That last row is the rule to follow when a new Bambu-specific behaviour is added: translate
the value you are about to compare, never the table you compare it against. The shipped data
those sites read is Bambu's and stays verbatim — white_fila_ids in
resources/printers/filaments_blacklist.json, the calibration id lists in
resources/printers/<model>.json, fila_id in
resources/profiles/BBL/filament/filaments_color_codes.json.
tests/slic3rutils/test_bambu_filament_ids.cpp covers the lookups, the payload rewrite and the
Bambu-specific rules. orcaslicer_discover_tests registers a Catch2 tag as a CTest label,
not as part of the test name, so -R matches nothing here and the filter is -L:
ctest --test-dir <build dir>/tests/slic3rutils -L BambuFilamentIds
Three places the map deliberately does not reach
The map and its lookups live in the GUI library, which libslic3r cannot link against and which a GUI-less build does not link at all. Three consequences are known and documented; none is worth pulling the map down into libslic3r for.
- The support display type in
PrintConfig.cpp.DynamicPrintConfig::get_filament_typepicksPLA-S/Sup.PLAandPA-S/Sup.PAfor a support filament by testingfilament_idagainstGFS00andGFS01, and otherwise falls back onfilament_type— a fallback that returns those same two pairs for"PLA"and"PA". Bambu Support W inheritsfdm_filament_plaand Bambu Support G inheritsfdm_filament_pa, so with theirOFids the fallback produces exactly what the id branches produced. (The only config that ever carries a singularfilament_idkey is the AMS tray config built inPlater.cpp, and that one never reaches this function.) These two lines are the only mention of a Bambu id anywhere in libslic3r, and they need no change. - Config imports.
PresetBundle::import_presets(File ▸ Import ▸ Import Configs, for.json/.zip/.orca_filament/.orca_printer/.orca_bundle) andPresetBundle::load_config_file(the CLI's--load-settingsof a G-code file with an embedded config) both parse inside libslic3r, out of the GUI's reach, so a Bambu id carried in such a file lands on the imported preset untranslated. The effect is bounded: that preset does not auto-match an AMS tray while the stale id is live, and the id does not survive being saved —Preset::savewrites afilament_idkey only for a preset whoseinheritsis empty, and on the next load an inheriting preset takes its parent's id. A known gap, and not a regression: nothing forwarded a stale id before either. - A build configured without the GUI.
target_link_libraries(OrcaSlicer libslic3r_gui)sits insideif (SLIC3R_GUI)insrc/CMakeLists.txt, so the lookups are not linkable when the GUI is off. The CLI's 3mf writer insrc/OrcaSlicer.cpptherefore guards its translation with#ifdef SLIC3R_GUI, and a 3mf that such a build slices for a Bambu printer carries ourOFids inslice_info.configrather than Bambu's. Every shipped build enables the GUI, so this reaches only a purpose-built GUI-less binary.
One more thing worth recording before it is rediscovered:
SyncAmsInfoDialog::update_print_required_data is a structural twin of the SD-card function
above and carries no translation. It has no callers today and its plate list is only ever read
for printer_model_id, so it is not a live gap — but wiring it up without adding the reverse
map would silently reproduce the bug.
How CI enforces this
Profile CI (check_profiles.yml) runs check_filament_ids() tree-wide via
scripts/orca_extra_profile_check.py. Its ground truth is
scripts/filament_id_snapshot.json — the sanctioned state: the id state derived from the
tree must equal the snapshot exactly, in both directions. Any change to the id landscape
therefore surfaces as a diff to that file, and that snapshot diff is what maintainers review
and gate in a PR. Never edit the snapshot by hand — --update-snapshot regenerates it
deterministically (running it twice changes nothing). The snapshot holds one map, ids: each
entry is the product the id is minted from (filament_vendor, filament_type, name) and the
filaments claiming it (Vendor/Filament), and it sanctions state, never exceptions: no check
consults it to excuse a preset from a rule, and there is no grandfather list of any kind.
The checks, in brief:
- Format — every id occurring in the tree is
OF+ 6 base62 chars. No exceptions: not a snapshot entry, not BBL. - Snapshot equality — tree claims == snapshot claims and each id's declared triple ==
its snapshot entry, both directions: any
filament_vendor/filament_type/name change surfaces as a snapshot diff. - Identity — the id is a function of the triple alone. A declared
OF*id must equal the one id its declarer's own triple mints, with no second acceptable value; the id an instantiated preset inherits must equal the mint of its own triple, however it inherits it (a root, a real filament, a library preset — structure rule 1); and every instantiated system filament must resolve an effective id at all (recall: an id-less one is a hard load error in C++ that discards the whole vendor bundle); and no two products mint one id (a base62 collision, resolved by renaming one of them). The errors print the expected id. - Reserved namespaces —
GF*,QD_*,P<7-hex>or"null"claimed by any vendor, BBL and Qidi included. - Triple integrity — every declarer must resolve a non-empty
filament_vendorandfilament_type(generics use"Generic"), and all declarers of one filament within a bundle must agree on the triple. - Bambu catalog map —
resources/printers/bambu_filament_ids.jsonparses, carries itssource/bambustudio_commit/generatedheader, keys onlyOF-format ids, maps each Bambu id at most once, and agrees with the tree on the triple of every row whose key the tree claims. See The Bambu catalog map; the remedy is always to regenerate, never to hand-edit.
A profile that declares a reserved-namespace id — GF*, QD_* or P<7-hex>, whatever
its vendor — cannot pass the format check, so --update-snapshot refuses to sanction it
rather than hide the mistake until CI. For a Bambu-cataloged product, the catalog map is where
the correspondence belongs. Any other new sharing via a declared id is caught by the identity
check; sharing through inheritance carries no declaration to check and surfaces only as a new
claim in the snapshot diff — which is exactly why that diff is the gate.
orca_extra_profile_check.py separately holds every declared id to the AMS 8-character limit,
tree-wide and for every vendor alike, scoped to the presets a vendor's index actually
references (a file the index never loads cannot break AMS matching).
Complementing the Python checks, CI also runs the C++ profile validator with -f
(check_filament_subtypes): it loads the bundle exactly as the app does and flags any printer
for which two or more compatible filament presets share one filament_id — the runtime-shaped
ambiguity check behind structure rule 3.
FAQ
- A new color of an existing product? Never a new id — colors are not filaments.
- A second diameter (1.75 mm and 2.85 mm) of the same product? A sibling filament with its own id: two diameters are separately selectable spool products.
- A high-speed tune of an existing material for another printer model? Same filament: keep the base name and inherit its root; no id key needed.
- A tuned generic ("our profile for Generic PLA")? Inherit
Generic PLA @System, keep theGeneric PLAbase name, setcompatible_printers; no id key needed. - A branded filament that borrows a generic's settings? Fine — inherit
Generic X @System(or any real filament) for the settings and declare the id of your own filament; runpython scripts/orca_id_tool.py --generateto mint it. Inheritance never changes the id. - I need to fix a filament's
filament_vendororfilament_type. Fix the config, run--generate --vendor <Vendor>(preview with--dry-run), then--update-snapshot, and commit the profile and snapshot diffs together. The id re-derives from the corrected identity, and nothing forwards the old value, so a tray or record still holding it falls back to matching by filament type. - I need to rename a filament. Rename the presets (adding
renamed_from, which keeps the preset name resolving), then--generate --vendor <Vendor>(preview with--dry-run), then--update-snapshot. The id follows the new filament name; as with any identity fix, the old id is not forwarded. - Can I reuse a
QD_*id for a Qidi profile? No — nobody can. It is the device protocol's own id space: the box composes those values at runtime and no preset carries one. Author Qidi filaments like any other vendor's. - CI says my filament needs an id. Run
python scripts/orca_id_tool.py --generate, then--update-snapshot, and commit both diffs. Do not type an id by hand.
For general profile authoring, see the profile development guide on the OrcaSlicer wiki.