{"record":{"id":"d10965b6af7fb250","repo":"apache/hadoop","slug":"filters-cannot-be-xor-ed","errorCode":null,"errorMessage":"filters cannot be xor-ed","messagePattern":"filters cannot be xor-ed","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/bloom/BloomFilter.java","lineNumber":180,"sourceCode":"\n  @Override\n  public void or(Filter filter) {\n    if(filter == null\n        || !(filter instanceof BloomFilter)\n        || filter.vectorSize != this.vectorSize\n        || filter.nbHash != this.nbHash) {\n      throw new IllegalArgumentException(\"filters cannot be or-ed\");\n    }\n    bits.or(((BloomFilter) filter).bits);\n  }\n\n  @Override\n  public void xor(Filter filter) {\n    if(filter == null\n        || !(filter instanceof BloomFilter)\n        || filter.vectorSize != this.vectorSize\n        || filter.nbHash != this.nbHash) {\n      throw new IllegalArgumentException(\"filters cannot be xor-ed\");\n    }\n    bits.xor(((BloomFilter) filter).bits);\n  }\n\n  @Override\n  public String toString() {\n    return bits.toString();\n  }\n\n  /**\n   * @return size of the the bloomfilter\n   */\n  public int getVectorSize() {\n    return this.vectorSize;\n  }\n\n  // Writable\n","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/bloom/BloomFilter.java#L162-L198","documentation":"BloomFilter.xor(Filter) symmetric-differences another filter's bits into this one and enforces the same compatibility rule as and()/or(): the operand must be a non-null BloomFilter with identical vectorSize and nbHash. Because xor mixes bit patterns, shape mismatch would corrupt the filter's semantics, so it fails with IllegalArgumentException('filters cannot be xor-ed') rather than producing garbage.","triggerScenarios":"filterA.xor(filterB) across filters built with different vectorSize/nbHash; filterA.xor(null); passing a Filter-typed object of another concrete class.","commonSituations":"Diff-style computations (what changed between two snapshots) where one snapshot's filter was created earlier with different settings; filters deserialized from stores written with different versions/configs.","solutions":["Ensure both filters share vectorSize and nbHash by constructing them from the same parameters.","If one filter came from serialized storage, compare its getVectorSize()/nbHash with the local one before xor.","Rebuild the older filter from source data with current parameters instead of xoring mismatched shapes.","Wrap xor in a shape check and fail with a descriptive error including both sizes."],"exampleFix":"// before\na.xor(b); // b built with vectorSize 512 vs a's 1024\n// throws: filters cannot be xor-ed\n\n// after\nif (b.vectorSize == a.vectorSize && b.nbHash == a.nbHash) {\n  a.xor(b);\n} else {\n  throw new IllegalStateException(\"filter shape mismatch: rebuild snapshot\");\n}","handlingStrategy":"validation","validationCode":"if (b != null && b.vectorSize == a.vectorSize && b.nbHash == a.nbHash) {\n  a.xor(b);\n} else {\n  throw new IllegalStateException(\n      \"Cannot xor filters of different shapes; rebuild snapshots\");\n}","typeGuard":"static boolean xorCompatible(BloomFilter a, BloomFilter b) {\n  return b != null && b.vectorSize == a.vectorSize && b.nbHash == a.nbHash;\n}","tryCatchPattern":null,"preventionTips":["Persist vectorSize/nbHash with each serialized filter and compare before xor.","Recreate snapshot filters from raw data instead of diffing mismatched shapes.","Unit-test xor paths with equal-shape fixtures only."],"tags":["bloom-filter","incompatible-parameters","merge","hadoop-common"],"backgroundTag":"bloom-filter-incompatible","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}