Skip to content

Escape hatches

Two doors. Not the front entrance.

rawNode

For a class your defs snapshot cannot describe (missing from /object_info, or /object_info is junk).

ts
const n = g.rawNode(
  "SomeUnregisteredNode",
  { model: ckpt.MODEL, strength: 0.5 },
  { outputs: [{ name: "MODEL", type: "MODEL" }], id: "9" },
);
n.out(0);

Params are validated structurally only. Imports of unknown custom nodes produce exactly this, with original JSON on node.source.

rawNode is not how you consume custom nodes. Snapshot + codegen is. If the class appears in live /object_info, regenerate wrappers and g.add(spec, …).

rawNode is also not remote code execution. The Python still lives in Comfy. You are only naming a class the type system does not know.

unsafe

ts
import { unsafe } from "@stepupgaming/comfy-workflows";
g.add(spec, { clip: unsafe(ckpt.MODEL) });

Widens any output to any input. Use it when a custom node lies about socket types. It does not turn off combo checks, range checks, cycle detection, or unbound-param errors.

What they do not mean

PhraseReality
"custom node"Generate wrappers from /object_info
"skip validation"There is no such flag
"the compiler will guess"It will not. E_UNRESOLVED_BYPASS instead
ts
import { unsafe, workflow } from "@stepupgaming/comfy-workflows";
import { loaders } from "@stepupgaming/comfy-workflows/nodes";

/**
 * rawNode is for classes your defs snapshot cannot describe.
 * It is not the normal custom-node path. Prefer codegen from /object_info.
 */
export function unknownClassGraph() {
  const g = workflow("escape");
  const ckpt = g.add(loaders.CheckpointLoaderSimple, {
    ckpt_name: "v1-5-pruned-emaonly.safetensors",
  });
  const mystery = g.rawNode(
    "SomeUnregisteredNode",
    { model: ckpt.MODEL, strength: 0.5 },
    { outputs: [{ name: "MODEL", type: "MODEL" }] },
  );
  void mystery.out(0);
  void unsafe(ckpt.MODEL);
  return g.toGraph();
}

Released under the MIT License. Unofficial project. Not affiliated with or endorsed by Comfy Org.