| name | rgthree |
| description | Configure and author rgthree-comfy nodes — Fast Groups Bypasser/Muter (group toggles), Power Lora Loader, Context/Context Big, Seed, Any Switch. Use when a workflow contains rgthree nodes, when asked to add stage/section toggles or an A/B switch, when stacking LoRAs, or when an rgthree node needs configuring. Covers the frontend-only nodes that are absent from /object_info and the properties-not-widgets configuration model. |
rgthree-comfy
rgthree-comfy is one of the most widely installed packs, so its nodes turn up in a
large share of community workflows. Three of its properties make it a recurring
agent-failure mode — all three fail quietly enough to look like success.
The three things that catch agents out
1. Some rgthree nodes are FRONTEND-ONLY. They are registered by the pack's JS
(registerCustomNodes()), have no Python class, and are therefore absent from
/object_info by design. Checking /object_info and concluding "this node doesn't
exist" is wrong. /object_info lists 24 rgthree backend types; the toggles are not
among them.
2. They are configured through PROPERTIES, not widgets. matchTitle,
toggleRestriction and sort live in node.properties (right-click → Properties),
not in widgets. Use panel_set_property. panel_set_widget does not silently
half-work — it refuses, with has no widget "matchTitle" (available: …) listing
the widgets that do exist. That refusal is the fastest confirmation you are on the
properties path; read it rather than retrying the write.
3. Fast Groups nodes take NO wiring and enumerate GROUPS by title. Leave the
OPT_CONNECTION output unconnected. The node renders one toggle per matching group,
so the groups must exist and be named before the node is useful.
Which rgthree nodes panel_add_node will actually add
The panel authorizes every add against fresh /object_info and fails closed on a
type it cannot find. Genuinely frontend-only types are exempt only via an explicit
allowlist (FRONTEND_ONLY_NODE_TYPES), so the exemption covers seven rgthree
types and no others:
Frontend-only, panel_add_node WORKS | Frontend-only, panel_add_node REFUSES |
|---|
Fast Groups Bypasser (rgthree) | Bookmark (rgthree) |
Fast Groups Muter (rgthree) | Mute / Bypass Relay (rgthree) |
Fast Bypasser (rgthree) | Mute / Bypass Repeater (rgthree) |
Fast Muter (rgthree) | Fast Actions Button (rgthree) |
Node Collector (rgthree) | Random Unmuter (rgthree) |
Label (rgthree) | |
Reroute (rgthree) | |
A refusal in the right-hand column is the guard working as designed, not a broken
pack and not something to work around — the node has no backend def and is not on the
allowlist. Say so and pick a different approach (the Bypasser/Muter cover almost every
real toggle need). Everything with a Python class — Power Lora Loader, Context*, Seed,
Any Switch, Power Prompt, Image Comparer — is a normal backend node and adds normally.
Fast Groups Bypasser / Muter
Fast Groups Bypasser (rgthree) sets the nodes of a group to bypass (mode 4 —
the node is skipped and its input passes through). Fast Groups Muter (rgthree) sets
them to mute (mode 2 — the node does not execute and everything downstream
dies). Prefer the Bypasser for toggling an optional stage inside a chain; reach
for the Muter only when you genuinely want to stop a branch.
All of the following are node properties — set them with panel_set_property:
| Property | Values | Default | Notes |
|---|
matchTitle | regex, case-insensitive | "" | Set this. Empty means every group in the workflow becomes a toggle. It is a real regex matched unanchored against the group title, so anchor it (^STAGE) or it matches mid-title. |
matchColors | comma-separated colors | "" | Alternative filter; pairs with a color convention. |
toggleRestriction | default / max one / always one | default | Both non-default values enforce mutual exclusion (they are matched on the substring " one"). Do NOT set either if the user may ever want all stages on in one queue. |
sort | position / alphanumeric / custom alphabet | position | The default means moving a group on the canvas silently reorders the toggles. alphanumeric is stable — prefer it. |
customSortAlphabet | string | "" | Only read when sort is custom alphabet. |
showNav | bool | true | Per-row jump-to-group arrow. |
showAllGraphs | bool | true | Include groups that live inside subgraphs. |
matchTitle does not rebuild the toggle list on the first write
panel_set_property stores matchTitle (and matchColors / sort / …) and
the reply from/to is truthful. Fast Groups nodes do not implement
onPropertyChanged. The toggle list is rebuilt by rgthree's
refreshWidgets() on a service tick (~8 ms after add, then every ~500 ms),
and leftover-row removal increments the index while splicing — a 22-group
list can stall at 13 with non-matching Enable … rows still present. A
never-drawn node can also come back as widgets:{} — that means the list
has not been built yet, not that there are no matching groups.
panel_query_graph also keys widgets by name, and every toggle is named
RGTHREE_TOGGLE_AND_NAV, so a built list collapses to one key.
Do this:
- Set
matchTitle immediately after panel_add_node (before the first
unfiltered refresh paints every group).
- Re-read with
panel_query_graph {ids:[<id>], fields:'detail'}.
- If
widgets is empty or the canvas still shows Enable rows that do
not match the regex, set matchTitle again. Do not delete and
re-add the node — that is slower and still needs a second set.
Recipe — make pipeline stages toggleable
-
panel_create_group per stage, with a prefixed title (STAGE 1 — …) so one
anchored regex selects exactly the intended set.
-
Verify group membership before you trust it. Group membership is purely
geometric: LiteGraph counts a node as a member when its centre falls inside
the box, and the auto-fit box around your node_ids will happily swallow unrelated
neighbours. When the live members differ from what you asked for, the result
carries extra_node_ids, missing_node_ids and a warning alongside
requested_node_ids — read them. (They appear only when you passed node_ids
and something differs, so their absence is a real all-clear.)
A stray node here is not cosmetic: toggling one stage will disable part of another.
To fix it, move the nodes apart (panel_edit_node, or panel_auto_layout) so
the regions are contiguous, or set an explicit bounds with panel_edit_group —
then re-check. panel_move_group does not help: by default it drags the
contained nodes along with the box, so the same nodes stay inside it.
-
panel_add_node(class_type="Fast Groups Bypasser (rgthree)"). Leave its output
unwired. Add nodes one at a time, not as a parallel batch.
-
panel_set_property → matchTitle = ^STAGE, and sort = alphanumeric.
Set them immediately after the add, then re-read the node. If widgets is
empty or leftover Enable rows remain, set matchTitle again — do not
delete and re-add.
-
Toggle, then verify with panel_graph_outline — it tags nodes [bypass] / [mute].
Power Lora Loader (rgthree)
A backend node (present in /object_info) that stacks N LoRAs in one node. Each row
is a widget named lora_1, lora_2, … whose value is a composite object
{on: bool, lora: "subdir\\name.safetensors", strength: float, strengthTwo: float|null}
(strengthTwo is the separate CLIP strength, null in the simple view). Rows are
identified by the presence of a lora key, and the node's control widgets are
appended after them, so do not index positionally — address the row by name.
Rows CAN be created programmatically. A freshly added Power Lora Loader has
no lora_N widgets at all — the on-canvas "➕ Add Lora" button opens a chooser on a
mouse event that no panel tool can press, but panel_set_widget does not need it:
writing lora_1 (then lora_2, …) with a JSON object STRING creates the row and
the reply carries created_widget: "lora_1". Create rows in order, one call each:
panel_set_widget(node_id=<id>, widget="lora_1",
value='{"on":true,"lora":"subdir/turbo.safetensors","strength":1,"strengthTwo":null}')
# A Windows subdir separator is a JSON escape — write it DOUBLED in the string:
# "lora":"Anima\\Tools\\turbo.safetensors"
Re-read with panel_query_graph {ids:[<id>], fields:'detail'} to confirm the row
landed. If you need a stack from scratch, create lora_1, then lora_2, and so on;
do not assume a skipped number is the next row.
On an existing row, write ONE field with dotted sub-field addressing — it merges
onto the current object and preserves every other field:
panel_set_widget(node_id=<id>, widget="lora_1.strength", value=0.8)
panel_set_widget(node_id=<id>, widget="lora_2.on", value=false)
panel_set_widget(node_id=<id>, widget="lora_1.lora", value="style/foo.safetensors")
Writing a bare scalar to lora_1 itself is the trap: it would set one field and
null the rest, so it is refused. To change several fields at once, pass a JSON
object STRING (the value argument accepts only string/number/boolean, so a literal
object fails tool validation before any write) — it is parsed and merged:
panel_set_widget(node_id=<id>, widget="lora_1", value='{"on":false,"strength":0.6}')
Fields are schema-checked: on non-nullable boolean, strength non-nullable number,
lora nullable string, strengthTwo nullable number. An unknown field name (a typo like
lora_1.strenght) is refused, not silently created, and nested paths are unsupported.
Clearing a nullable field takes the JSON-string form, not a dotted write. The panel
accepts null for lora / strengthTwo, but value is typed string | number | boolean, so a bare value=null is rejected by tool-arg validation before any write
happens — the same schema limit as the whole-row case above. Clear it through the string:
panel_set_widget(node_id=<id>, widget="lora_1", value='{"lora":null}')
Turning a LoRA off (lora_N.on = false) is usually safer than clearing it anyway.
Other commonly-seen rgthree nodes
- Context / Context Big / Context Switch / Context Merge — bundle
MODEL/CLIP/VAE/conditioning into one
RGTHREE_CONTEXT wire. These are NOT virtual
wiring. Unlike Get/Set buses they are real executable backend nodes, so
panel_strip_workflow and panel_flatten_workflow deliberately keep them — they
run. Do not expect either tool to dissolve a Context chain. There is no hidden edge to
resolve: every link is a real link, traceable with panel_query_graph. What a Context
hides is which field a downstream node pulls out of the bundle, so read the chain
node by node. (panel_slice_workflow still carves one pipeline out of a toggled
monolith, and panel_strip_workflow still resolves any genuine Get/Set buses and
Reroutes around it.)
- Seed (rgthree) — it deletes the built-in
control_after_generate widget on
creation, so do not try to write it; the widget is not there. Control is by SPECIAL
SEED VALUES written to the seed widget instead: -1 randomize, -2 increment,
-3 decrement. Any concrete seed is returned unchanged — 42 stays 42 every
run — so to pin a run, write the number; to re-randomize, write -1. The frontend
resolves a special value into a real seed before queueing and shows the result in a
read-only last_seed widget, so a seed re-read after a run may not be the special
value the user set (a fixed seed, however, is stable).
- Any Switch (rgthree) — the first non-null input wins; a common A/B toggle paired
with bypassed branches. An empty Context counts as null, so an unfilled Context
branch is skipped rather than selected.
Gotchas
- A bypassed node is skipped and passes its input through; a muted node kills
everything downstream. Choosing the Muter where the Bypasser was meant breaks the
chain rather than shortening it.
- Always
panel_graph_outline before a run — a stale toggle is a top cause of a wrong
render, and the outline marks [bypass] / [mute] explicitly.
- rgthree's
Bookmark nodes respond to keypresses and are inert to agents.
Sources
- Official: https://github.com/rgthree/rgthree-comfy
- Empirical: frontend-only allowlist and properties-not-widgets notes verified against the installed pack and the panel guard.
#1808 matchTitle rebuild: Fast Groups have no onPropertyChanged; refreshWidgets() leftover removal is removeWidget(index++) in fast_groups_muter.ts; first unfiltered tick is scheduled from addFastGroupNode in fast_groups_service.ts.