{"record":{"id":"bdda36518fc80f66","repo":"trekhleb/javascript-algorithms","slug":"one-or-two-values-are-not-in-sets","errorCode":null,"errorMessage":"One or two values are not in sets","messagePattern":"One or two values are not in sets","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/data-structures/disjoint-set/DisjointSet.js","lineNumber":58,"sourceCode":"      return null;\n    }\n\n    return requiredDisjointItem.getRoot().getKey();\n  }\n\n  /**\n   * Union by rank.\n   *\n   * @param {*} valueA\n   * @param {*} valueB\n   * @return {DisjointSet}\n   */\n  union(valueA, valueB) {\n    const rootKeyA = this.find(valueA);\n    const rootKeyB = this.find(valueB);\n\n    if (rootKeyA === null || rootKeyB === null) {\n      throw new Error('One or two values are not in sets');\n    }\n\n    if (rootKeyA === rootKeyB) {\n      // In case if both elements are already in the same set then just return its key.\n      return this;\n    }\n\n    const rootA = this.items[rootKeyA];\n    const rootB = this.items[rootKeyB];\n\n    if (rootA.getRank() < rootB.getRank()) {\n      // If rootB's tree is bigger then make rootB to be a new root.\n      rootB.addChild(rootA);\n\n      return this;\n    }\n\n    // If rootA's tree is bigger then make rootA to be a new root.","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/trekhleb/javascript-algorithms/blob/85293e3e2b88f4d2ce330d956b139cf628aa1e82/src/data-structures/disjoint-set/DisjointSet.js#L40-L76","documentation":"Thrown by DisjointSet.union() when find() returns null for valueA or valueB (src/data-structures/disjoint-set/DisjointSet.js:54-59). find() builds a probe DisjointSetItem, derives its key via the constructor's keyCallback (or the raw value), and returns null when that key is absent from this.items - meaning the value was never registered with makeSet(). Union-find can only merge values that already belong to some set, so an unknown operand is treated as a programmer error rather than being silently auto-created.","triggerScenarios":"Calling ds.union(a, b) before ds.makeSet(a) and ds.makeSet(b) have both run. Concrete cases: a hand-written Kruskal loop that unions edge endpoints but never bootstraps makeSet() over all vertices; passing vertex.value to makeSet() but the GraphVertex object to union() (or vice versa) so the derived keys differ; a keyCallback that produces different key shapes at registration time and merge time.","commonSituations":"Reimplementing kruskal or detectUndirectedCycleUsingDisjointSet instead of using the shipped helpers (which do the makeSet bootstrap for you); refactoring vertex values from primitives to objects without adding a keyCallback; pipelines where a late-arriving node is merged before it is registered.","solutions":["Bootstrap every value before merging: graph.getAllVertices().forEach((v) => ds.makeSet(v)); - this mirrors what kruskal() does internally.","If values are objects, construct the set with a stable key callback: const ds = new DisjointSet((v) => v.id); and use the same value shape for makeSet() and union().","Guard lazily when membership is uncertain: if (ds.find(a) === null) ds.makeSet(a); if (ds.find(b) === null) ds.makeSet(b); before ds.union(a, b).","Verify you pass the identical value/reference to both makeSet() and union(); inspect Object.keys(ds.items) to see the registered keys."],"exampleFix":"// before\nconst ds = new DisjointSet((v) => v.id);\nds.union(nodeA, nodeB); // Error: One or two values are not in sets\n\n// after\nconst ds = new DisjointSet((v) => v.id);\ngraph.getAllVertices().forEach((vertex) => ds.makeSet(vertex));\nds.union(nodeA, nodeB);","handlingStrategy":"validation","validationCode":"// find() returns null for values not in any set - cheap pre-check\nconst inSomeSet = (disjointSet, value) => disjointSet.find(value) !== null;\n\nif (inSomeSet(ds, valueA) && inSomeSet(ds, valueB)) {\n  ds.union(valueA, valueB);\n} else {\n  ds.makeSet(valueA); // or handle the unknown value explicitly\n  ds.makeSet(valueB);\n}","typeGuard":null,"tryCatchPattern":"try {\n  ds.union(valueA, valueB);\n} catch (error) {\n  if (error.message === 'One or two values are not in sets') {\n    // One operand was never makeSet()-ed: register it or skip this pair.\n  } else {\n    throw error;\n  }\n}","preventionTips":["Run one makeSet() loop over the whole input right after constructing the DisjointSet, before any union().","Give the constructor a keyCallback when values are objects, so keys do not depend on identity or string coercion.","Use the same value (or reference) for registration and merging - never mix a GraphVertex with its .value.","Prefer the library's kruskal() / detectUndirectedCycleUsingDisjointSet(), which register every vertex for you."],"tags":["disjoint-set","union-find","precondition","make-set","kruskal"],"backgroundTag":"key-not-found","analyzedSha":"85293e3e2b88f4d2ce330d956b139cf628aa1e82","analyzedAt":"2026-08-24T05:59:10.417Z","schemaVersion":2},"datasetVersion":"2026-08-24T07:17:09.176Z"}