PatchWork Import Bugs¶
The PatchWork web UI throws a generic r.trim is not a function toast for most schema-mismatch failures, with no useful stack trace. This page is the catalog of known causes so you can identify which one you're hitting.
Every .nbflow we produce should be patched against this list before handing back to the user. The pre-generation sanity check automates most of these checks.
Bug 1: Dynamic dynamicRows must be flat strings, not objects¶
Symptom- Toast:
r.trim is not a functionon import. Cause- The Dynamic Prompt's
dynamicRowsis[{label: "Scene 01", text: "<prompt>"}, ...]— array of objects. PatchWork's Prompt node callsr.trim()on each row directly and crashes whenris an object. Fix- Flatten every dynamic row from
{label, text}to justtext. The label info is redundant (the node title carries the human-readable name).
for n in nodes.values():
if n.get('type') == 'nanobanana/Prompt' and n.get('properties', {}).get('dynamicMode'):
rows = n['properties'].get('dynamicRows', [])
n['properties']['dynamicRows'] = [
r['text'] if isinstance(r, dict) else r
for r in rows
]
Bug 2: Template input slot name must equal the placeholder token¶
Symptom- Connection between Dynamic Prompt and Template Prompt appears in the file data but the UI shows them as disconnected. Generation receives the literal
{placeholder}string. Cause-
The Importer creates template Prompts with
inputs: [{name: "input", ...}](generic name). PatchWork runs_syncTemplateInputs()on every import:- Parses
{placeholder}from the template text - Removes any input slot whose name doesn't match a placeholder — destroying the connected link in the process
- Adds a fresh empty slot named after the placeholder
So
name: "input"+ template{scene}→ input gets removed, link is destroyed, an emptysceneslot is added → dynamic-to-template connection lost. - Parses
Fix-
Rename input slots on every template Prompt to match the placeholder token in the template text. Both
nameandlabelshould equal the placeholder.Template type Input slot name Image scene template sceneVeo speaking template dialogue(or whichever placeholder is used)Veo speaking with pose dialogue+poseB-roll image template brollB-roll Veo template motion
Bug 3: Veo3 needs 3 input slots with spaces, not 2 with underscores¶
Symptom- Toast:
r.trim is not a functionon import. Or third slot silently missing in the UI. Cause-
The Importer emits 2 inputs with underscored names (
start_frame). Canonical Veo3 inputs are:"inputs": [ {"name": "prompt", "type": "string", "link": null}, {"name": "start frame", "type": "*", "link": null}, {"name": "end frame", "type": "*", "link": null} ]All three slots must exist with spaces in the names, not underscores. The
end frameslot haslink: nullwhen unwired — but the slot itself must exist. Fix- Restore the canonical 3-input shape on every Veo3 node. Use a known-good sibling
.nbflowas a template if needed.
Bug 4: Veo3 needs model and negativePrompt properties¶
Symptom- Toast:
r.trim is not a function. Cause- The Importer leaves
negativePromptundefined. PatchWork's Veo3 code calls.trim()on it during import → crash. Fix- Set
negativePrompt: ""(empty string is fine — must be a string, not undefined). Also fillmodel: "veo-3.1-fast-generate-preview"(or the current Veo identifier),inputVariable,resolution,seed,imageCount,sampleCount,repeatCount,timeout, and the boolean UI flags (_advancedOpen,_disabled,_pinned,_savedGalleryIndex).
Bug 5: Template Prompt nodes need variableName and dynamicRows¶
Symptom.nbflowimports but template Prompt node behaves oddly —onConfiguremay try to read missing properties.Cause- Templates carry
variableName(string) anddynamicRows: [""]even though they're not in dynamic mode. The Importer often omits both. Fix- Fill defaults on every template Prompt.
Bug 6: Output links arrays must be the canonical source of truth¶
Symptom- Stale
output.linksarrays reference link IDs that don't originate from that output. Cause- After a structural edit (fan-out renumber, variant patch, manual edit),
output.linksarrays don't get resynced from thegraphData.linkstable. Fix- After any structural edit, rebuild every
output.linksfrom the central links by scanning for(src_node_id, src_slot)matches. Usemanager/scripts/_lib_link_refs.py(resync_link_refs(tab)).
Bug 7: Reference Media nodes need 1 input slot (with link: null)¶
Symptom- Avatar reference Media node imports but the UI shape is wrong.
Cause- Canonical Media node has
inputs: [{name: "input", type: "*", link: null}]. The Importer sometimes emits emptyinputs: []. Fix- Add the empty input slot to match canonical shape — even though refs are data sources, the slot must exist for shape consistency.
Bug 8: NanobananaAPI input slot is image 1 (with space)¶
Symptom- Connection from a Media node to a NanobananaAPI's image input slot fails or maps to the wrong slot.
Cause- The Importer emits
image_1andimage_2(underscores). Canonical isimage 1andimage 2(spaces). Fix- Rename input slots to use spaces. Same fix pattern as Veo3's
start frameslot.
Stale per-node link refs (the renumber bug)¶
Symptom- In PatchWork UI, edges appear orphaned. When you run the workflow through the UI, reference images are NOT sent to G-Labs — generations come out looking like generic stock content, not matching the avatar's face or wardrobe.
Cause-
PatchWork stores edges in two places:
tab.graphData.links— the canonical central array. The headlessworkflow-runner.jsreads this.node.outputs[slot].links+node.inputs[slot].link— per-node refs. PatchWork's UI renderer reads these.
Any mutation script (renumbering, fan-out, variant patcher) that updates the central array but doesn't resync the per-node refs leaves the file in an inconsistent state:
- The connection exists in the data
- PatchWork's canvas renders the edge as orphan
- UI-driven generation walks the broken edge map and silently drops the reference
Fix-
Mandatory rule for any script that mutates
tab.graphData:from _lib_link_refs import resync_link_refs, assert_clean # ... do mutation work ... for tab in workflow['tabs']: resync_link_refs(tab) assert_clean(workflow) json.dump(workflow, f, indent=2)Helper lives at
manager/scripts/_lib_link_refs.py. The PatchWork Importer and every fan-out / variant / merge script must call this.
Cached Media swap bug¶
Symptom- After a successful Generation Runner pass, the visual workflow graph in PatchWork shows "missing connections" — the avatar reference Media node and any setting / establishing Approve gates appear to have no outgoing edges, even though the link entries still exist in the data.
Cause-
After a successful image generation, the runner replaces each
nanobanana/NanobananaAPInode with ananobanana/Medianode holding the generated URL inproperties.imageData.- Original API node had 3 input slots (
prompt,image 1,image 2) - Media node has 0 input slots
The runner preserves outgoing edges but does not prune the incoming edges from avatar reference / setting reference image inputs. Those links still target slot 1 and slot 2 of the swapped node — slots that no longer exist on the Media node.
- Original API node had 3 input slots (
Fix when manually rewiring a runner output-
- Use the pre-runner backup (in
backups/{workflow}-{version}.nbflow.bak-*-pre-runner) to recover the original NanobananaAPI node templates. - Replace each Cached Media node with the original NanobananaAPI structure (preserving the 3 input slots).
- Bake the cached image URL into
properties._savedImages = [url]so the cached output is preserved. - Set
properties.outputCount = 4andproperties.model = "nano_banana_2". - Re-run the link validator to confirm zero broken links.
- Use the pre-runner backup (in
Long-term fix- In the runner itself: set
_savedImageson the existing API node and skip the type swap entirely.
Recovery pattern¶
When an Importer-emitted file fails to import:
- Identify a known-good sibling
.nbflowfrom the same folder (or an older approved version). - Capture its per-node-type canonical
inputs/outputs/propertiesshape. - Merge those defaults into the broken file (preserving the actual link IDs and per-node values).
- Run the pre-generation sanity check to verify zero dangling links.
- Try the import again.
The PatchWork Importer agent itself is being patched to emit the canonical shape going forward. Until that work is complete, run the sanity check on every Importer-produced file before assuming it'll import cleanly.