{"record":{"id":"df71155fce53a295","repo":"infiniflow/ragflow","slug":"can-t-find-variable-cpn-id-var-nm","errorCode":null,"errorMessage":"Can't find variable: '{cpn_id}@{var_nm}'","messagePattern":"Can't find variable: '(.+?)@(.+?)'","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"agent/canvas.py","lineNumber":255,"sourceCode":"            last = m.end()\n\n        out_parts.append(value[last:])\n        return \"\".join(out_parts)\n\n    def get_variable_value(self, exp: str) -> Any:\n        exp = exp.strip(\"{\").strip(\"}\").strip(\" \").strip(\"{\").strip(\"}\")\n        if exp.find(\"@\") < 0:\n            return self.globals[exp]\n        # Split from the left with maxsplit=1 so the trailing var_nm can\n        # legitimately contain '@' characters (defensive: although the\n        # upstream regex in `get_value_with_variable` constrains `var_nm`\n        # to `[A-Za-z0-9_.-]+`, direct callers of this method may pass\n        # any string and should not raise `ValueError: too many values\n        # to unpack`). `cpn_id` is system-generated and never contains '@'.\n        cpn_id, var_nm = exp.split(\"@\", 1)\n        cpn = self.get_component(cpn_id)\n        if not cpn:\n            raise Exception(f\"Can't find variable: '{cpn_id}@{var_nm}'\")\n        parts = var_nm.split(\".\", 1)\n        root_key = parts[0]\n        rest = parts[1] if len(parts) > 1 else \"\"\n        root_val = cpn[\"obj\"].output(root_key)\n\n        if not rest:\n            return root_val\n        return self.get_variable_param_value(root_val, rest)\n\n    def get_variable_param_value(self, obj: Any, path: str) -> Any:\n        cur = obj\n        if not path:\n            return cur\n        for key in path.split(\".\"):\n            if cur is None:\n                return None\n\n            if isinstance(cur, str):","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/agent/canvas.py#L237-L273","documentation":"When the canvas resolves a variable reference of the form '{component_id@output.param.path}', it looks up the source component by id. If no component with that id exists in the DSL, this Exception is raised during get_variable_value — the reference points at a deleted or renamed node.","triggerScenarios":"Any component param containing a reference like '{abc123@output.text}' where 'abc123' is not a key in dsl['components'] — typical after deleting a node that other nodes still reference, or hand-editing component ids.","commonSituations":"Deleting an upstream component without cleaning references to it; copy-pasting nodes between canvases (ids don't travel with valid targets); race where the canvas is edited while a run is starting; typos in manually written reference expressions.","solutions":["Open the canvas and find the node whose param still references the deleted component id; rebind it to an existing component or delete the stale reference.","Search the DSL JSON for the cpn_id shown in the error to locate every stale reference.","If copy-pasting between workflows, re-wire the pasted node's references to components that exist in the target canvas.","Add a lint pass over the DSL (all '@' references resolve to existing component ids) before running."],"exampleFix":"# before\nparams = {\"content\": \"{deadbeef@output.text}\"}  # deadbeef was deleted\n\n# after\nparams = {\"content\": \"{live1234@output.text}\"}  # rebind to existing node","handlingStrategy":"validation","validationCode":"def check_references(dsl):\n    ids = set(dsl['components'].keys())\n    bad = []\n    for k, cpn in dsl['components'].items():\n        for v in extract_refs(cpn['obj'].get('params', {})):  # finds '{id@...}' tokens\n            if v.split('@', 1)[0] not in ids:\n                bad.append((k, v))\n    return bad  # empty list == safe","typeGuard":"def ref_resolves(exp: str, component_ids: set) -> bool:\n    return '@' not in exp or exp.strip('{} ').split('@', 1)[0] in component_ids","tryCatchPattern":"try:\n    value = canvas.get_variable_value(exp)\nexcept Exception as e:\n    if str(e).startswith(\"Can't find variable:\"):\n        # stale reference: surface which node/param held it and fail fast\n        raise WorkflowIntegrityError(str(e)) from e\n    raise","preventionTips":["Run a reference-lint pass over the DSL after every node deletion.","When deleting nodes, let the editor remove inbound references atomically.","Never hand-type component ids in reference expressions; pick from the variable picker."],"tags":["canvas","variable-resolution","stale-reference","workflow"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}