{"record":{"id":"93737053defa068b","repo":"apache/beam","slug":"ambiguous-renaming-of-tags","errorCode":null,"errorMessage":"Ambiguous renaming of tags.","messagePattern":"Ambiguous renaming of tags\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"sdks/typescript/src/apache_beam/transforms/external.ts","lineNumber":280,"sourceCode":"    ) {\n      for (const [id, proto] of Object.entries(src)) {\n        if (id.startsWith(namespace)) {\n          dest[id] = proto;\n        }\n      }\n    }\n\n    function difference<T>(a: Set<T>, b: Set<T>): T[] {\n      return [...a].filter((x) => !b.has(x));\n    }\n\n    // Some SDKs enforce input naming conventions.\n    const newTags = difference(\n      new Set(Object.keys(response.transform!.inputs)),\n      new Set(Object.keys(transformProto.inputs)),\n    );\n    if (newTags.length > 1) {\n      throw new Error(\"Ambiguous renaming of tags.\");\n    } else if (newTags.length === 1) {\n      const missingTags = difference(\n        new Set(Object.keys(transformProto.inputs)),\n        new Set(Object.keys(response.transform!.inputs)),\n      );\n      transformProto.inputs[newTags[0]] = transformProto.inputs[missingTags[0]];\n      delete transformProto.inputs[missingTags[0]];\n    }\n\n    // PCollection ids may have changed as well.\n    const renamedInputs = Object.fromEntries(\n      Object.keys(response.transform!.inputs).map((k) => [\n        response.transform!.inputs[k],\n        transformProto.inputs[k],\n      ]),\n    );\n    response.transform!.inputs = Object.fromEntries(\n      Object.entries(response.transform!.inputs).map(([k, v]) => [","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/typescript/src/apache_beam/transforms/external.ts#L262-L298","documentation":"After expansion, splice() reconciles input tag names between the original transformProto and the service's response.transform. If the response renamed tags such that more than one new (unrecognized) tag appears, the single-renaming heuristic cannot disambiguate, so it throws 'Ambiguous renaming of tags.'","triggerScenarios":"expandInternalAsync -> splice where Object.keys(response.transform.inputs) contains more than one tag not present in the original transformProto.inputs.","commonSituations":"A foreign-language SDK's expansion service renames multiple input tags to comply with its own naming conventions, breaking the TypeScript SDK's tag-rename inference.","solutions":["Use exactly one named input on the external transform, or align tag naming with what the expansion service produces.","Upgrade/patch the external SDK's expansion service so it does not rename multiple input tags.","Pre-map input tags before expansion so no renaming occurs."],"exampleFix":"// before\nexternal.withInputs({ a: pc1, b: pc2 }); // service renames both tags -> ambiguous\n// after\nexternal.withInputs({ input: pc1 }); // single tag, rename inference unambiguous","handlingStrategy":"try-catch","validationCode":"// Inspect the expanded response's input tags before splicing\nconst respTags = Object.keys(response.transform.inputs);\nconst origTags = Object.keys(transformProto.inputs);\nconst newTags = respTags.filter(t => !origTags.includes(t));\nif (newTags.length > 1) throw new Error(\"Multiple renamed input tags; adjust transform naming\");","typeGuard":"null","tryCatchPattern":"try {\n  await external.expandInternalAsync(...);\n} catch (e) {\n  if (e.message === \"Ambiguous renaming of tags.\") {\n    console.error(\"Expansion service renamed multiple inputs; use single-tag inputs or matching names.\");\n  } else throw e;\n}","preventionTips":["Use a single named input on external transforms when possible","Match input tag names to what the remote SDK produces","Keep external SDK versions aligned with the TypeScript SDK"],"tags":["external-transform","tags","cross-language"],"backgroundTag":"unexpected-response-shape","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}