{"record":{"id":"8c746287d94f03fb","repo":"langflow-ai/langflow","slug":"created-snapshot-binding-has-empty-tool-id-bindin","errorCode":null,"errorMessage":"Created snapshot binding has empty tool_id={binding.tool_id!r} or source_ref={binding.source_ref!r}; cannot map tool binding.","messagePattern":"Created snapshot binding has empty tool_id=(.+?) or source_ref=(.+?); cannot map tool binding\\.","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/mapper.py","lineNumber":1799,"sourceCode":"            payload[\"display_name\"] = display_name\n        return payload\n\n    def _to_api_created_tools(\n        self,\n        *,\n        adapter_created_snapshot_bindings: list[Any],\n    ) -> list[WatsonxApiCreatedTool]:\n        \"\"\"Map adapter created snapshot bindings to API ``created_tools``.\"\"\"\n        created_tools: list[WatsonxApiCreatedTool] = []\n        for binding in adapter_created_snapshot_bindings:\n            tool_id = str(binding.tool_id or \"\").strip()\n            source_ref = str(binding.source_ref or \"\").strip()\n            if not tool_id or not source_ref:\n                msg = (\n                    f\"Created snapshot binding has empty tool_id={binding.tool_id!r} or \"\n                    f\"source_ref={binding.source_ref!r}; cannot map tool binding.\"\n                )\n                raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=msg)\n            try:\n                created_tool = WatsonxApiCreatedTool(\n                    flow_version_id=source_ref,\n                    tool_id=tool_id,\n                )\n            except ValidationError as exc:\n                msg = f\"Created snapshot binding has non-UUID source_ref={source_ref!r} for tool_id={tool_id!r}.\"\n                raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=msg) from exc\n            created_tools.append(created_tool)\n        return created_tools\n\n    def _dump_key_value_connection_payloads(self, key_value_payloads: list[Any] | None) -> list[dict[str, Any]] | None:\n        if not key_value_payloads:\n            return None\n        normalized: list[dict[str, Any]] = []\n        for payload in key_value_payloads:\n            item: dict[str, Any] = {\"app_id\": payload.app_id}\n            environment_variables = self._to_adapter_environment_variables(payload.credentials)","sourceCodeStart":1781,"sourceCodeEnd":1817,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/mapper.py#L1781-L1817","documentation":"After a watsonx_orchestrate deployment update that creates new flow snapshots, the adapter returns created snapshot bindings and the mapper converts them into the API created_tools response. Each binding must carry both a non-empty tool_id (the wxO-owned tool id) and source_ref (the Langflow flow_version_id). If either is empty/blank after stripping, the binding cannot be mapped and HTTP 500 is raised — the provider or adapter returned malformed data.","triggerScenarios":"Deployment update succeeds on the wxO side but the adapter's created-snapshot binding payload contains an empty tool_id or source_ref; mapping the response then fails with 500.","commonSituations":"wxO API version change altering the binding payload shape; adapter bug or partial response; race where the provider returns a binding before assigning the tool id. Not caused by anything the API caller sent.","solutions":["Capture the raw adapter/wxO response (server logs) and verify what the provider actually returned for created snapshot bindings.","If tool_id is empty because the provider hasn't assigned it yet, the adapter must wait/re-fetch before returning bindings — file an adapter issue.","Check for a version mismatch between the wxO adapter and the watsonx_orchestrate API/region being targeted.","Retry the update: if the snapshot was in fact created, a follow-up update may re-sync bindings; otherwise clean up orphaned tools in wxO."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def is_valid_binding(binding) -> bool:\n    return bool(str(getattr(binding, \"tool_id\", \"\") or \"\").strip()) and bool(str(getattr(binding, \"source_ref\", \"\") or \"\").strip())","tryCatchPattern":"try:\n    created = await client.patch(f\"/api/v1/deployments/{id}\", json=body)\nexcept HTTPError as e:\n    if e.response.status_code == 500 and \"empty tool_id\" in e.response.text:\n        # provider-side partial success: audit wxO for orphaned tools before retrying\n        log.error(\"adapter returned malformed created bindings; check wxO tools\")\n    raise","preventionTips":["This is a server-side/provider fault — keep adapter and wxO API versions aligned.","Log the raw adapter response when it occurs so support can identify the malformed binding.","After a 500 on update, reconcile wxO tool state before retrying to avoid duplicates."],"tags":["watsonx-orchestrate","deployments","http-500","adapter","created-tools"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}