{"record":{"id":"3fb44149a5af9112","repo":"calesthio/OpenMontage","slug":"node-errors-json-dumps-data-node-errors","errorCode":null,"errorMessage":"Node errors: {json.dumps(data['node_errors'])}","messagePattern":"Node errors: (.+?)","errorType":"exception","errorClass":"ComfyUIError","httpStatus":null,"severity":"error","filePath":"tools/_comfyui/client.py","lineNumber":190,"sourceCode":"            return False\n\n    # ------------------------------------------------------------------\n    # Core cycle\n    # ------------------------------------------------------------------\n\n    def submit(self, workflow: dict) -> str:\n        \"\"\"Queue a workflow for execution.  Returns the ``prompt_id``.\"\"\"\n        resp = requests.post(\n            f\"{self.server_url}/prompt\",\n            json={\"prompt\": workflow, \"client_id\": self.client_id},\n            timeout=30,\n        )\n        try:\n            data = resp.json()\n        except ValueError:\n            data = {}\n        if data.get(\"node_errors\"):\n            raise ComfyUIError(f\"Node errors: {json.dumps(data['node_errors'])}\")\n        if data.get(\"error\"):\n            raise ComfyUIError(f\"Prompt error: {json.dumps(data['error'])}\")\n        resp.raise_for_status()\n        prompt_id = data.get(\"prompt_id\")\n        if not prompt_id:\n            raise ComfyUIError(f\"No prompt_id in response: {data}\")\n        return prompt_id\n\n    def poll(\n        self,\n        prompt_id: str,\n        *,\n        timeout: int = 600,\n        interval: int = 5,\n    ) -> dict:\n        \"\"\"Block until *prompt_id* finishes.  Returns the history entry.\"\"\"\n        deadline = time.time() + timeout\n        while time.time() < deadline:","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/_comfyui/client.py#L172-L208","documentation":"ComfyUIError raised when the ComfyUI /prompt submit endpoint responds with a non-empty node_errors object. ComfyUI validates every node's inputs server-side before queueing; node_errors maps node IDs to validation failures (missing required input, unknown widget value, bad type). The JSON is embedded verbatim so each offending node is identifiable. This happens before any execution, so no GPU time is wasted.","triggerScenarios":"POSTing a workflow via submit() where at least one node misses a required input, references an unlinked mandatory slot, uses a value the node no longer accepts, or the workflow JSON was exported from a different ComfyUI version with changed node schemas.","commonSituations":"Custom workflow_json/workflow_path input with hand-edited node ids; missing custom nodes on the server (validation of those nodes fails); ComfyUI upgraded and a node's input contract changed; LoraLoader pointing at a nonexistent lora name; wrong image format field types.","solutions":["Parse the embedded node_errors JSON — each key is the node id and the value names the failing input; fix the workflow at those nodes","Verify every custom node in the workflow is installed on the server (ComfyUI Manager) — missing nodes are a top cause","Re-export the workflow from the same ComfyUI instance you submit to (API format), then re-apply patches with patch_workflow","If a required input is optional server-side, connect it explicitly instead of leaving it absent"],"exampleFix":"# before\npatches = {\"6\": {\"ckpt_name\": \"sd_xl_base.safetensors\"}}  # model not present\n\n# after\n# check available checkpoints first: GET /object_info/CheckpointLoaderSimple\npatches = {\"6\": {\"ckpt_name\": \"sd_xl_turbo_1.0_fp16.safetensors\"}}","handlingStrategy":"validation","validationCode":"import requests\ninfo = requests.get(f\"{server_url}/object_info\", timeout=10).json()\n# verify every node's class_type exists and required inputs are wired\nfor nid, node in workflow.items():\n    spec = info.get(node[\"class_type\"])\n    if spec is None:\n        raise SystemExit(f\"node {nid}: unknown type {node['class_type']!r} (custom node missing?)\")\n    required = spec[\"input\"][\"required\"]\n    missing = [k for k in required if k not in node[\"inputs\"] and k not in spec[\"input\"].get(\"optional\", {})]\n    if missing:\n        raise SystemExit(f\"node {nid}: missing required inputs {missing}\")","typeGuard":"def workflow_node_ids_are_valid(workflow: dict, object_info: dict) -> bool:\n    return all(n[\"class_type\"] in object_info for n in workflow.values())","tryCatchPattern":"try:\n    prompt_id = client.submit(workflow)\nexcept ComfyUIError as e:\n    if \"Node errors\" in str(e):\n        # parse embedded JSON: {node_id: {errors...}}\n        raise SystemExit(f\"fix workflow nodes: {e}\")\n    raise","preventionTips":["Submit API-format exports only","Install all custom nodes via ComfyUI Manager before submitting","Validate workflows against GET /object_info before submit","Keep the export and the target server on the same ComfyUI version"],"tags":["comfyui","workflow-validation","api-client","node-errors"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}