{"record":{"id":"67c1354a0c204db7","repo":"apache/beam","slug":"cannot-encode-a-null-t-digest-sketch","errorCode":null,"errorMessage":"cannot encode a null T-Digest sketch","messagePattern":"cannot encode a null T-Digest sketch","errorType":"exception","errorClass":"CoderException","httpStatus":null,"severity":"error","filePath":"sdks/java/extensions/sketching/src/main/java/org/apache/beam/sdk/extensions/sketching/TDigestQuantiles.java","lineNumber":320,"sourceCode":"      return new MergingDigestCoder();\n    }\n\n    @Override\n    public void populateDisplayData(DisplayData.Builder builder) {\n      super.populateDisplayData(builder);\n      builder.add(DisplayData.item(\"compression\", compression).withLabel(\"Compression factor\"));\n    }\n  }\n\n  /** Coder for {@link MergingDigest} class. */\n  static class MergingDigestCoder extends CustomCoder<MergingDigest> {\n\n    private static final ByteArrayCoder BYTE_ARRAY_CODER = ByteArrayCoder.of();\n\n    @Override\n    public void encode(MergingDigest value, OutputStream outStream) throws IOException {\n      if (value == null) {\n        throw new CoderException(\"cannot encode a null T-Digest sketch\");\n      }\n      ByteBuffer buf = ByteBuffer.allocate(value.byteSize());\n      value.asBytes(buf);\n      BYTE_ARRAY_CODER.encode(buf.array(), outStream);\n    }\n\n    @Override\n    public MergingDigest decode(InputStream inStream) throws IOException {\n      byte[] bytes = BYTE_ARRAY_CODER.decode(inStream);\n      ByteBuffer buf = ByteBuffer.wrap(bytes);\n      return MergingDigest.fromBytes(buf);\n    }\n\n    @Override\n    public boolean isRegisterByteSizeObserverCheap(MergingDigest value) {\n      return true;\n    }\n","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/sketching/src/main/java/org/apache/beam/sdk/extensions/sketching/TDigestQuantiles.java#L302-L338","documentation":"The T-Digest coder's encode() throws CoderException when the MergingDigest value is null, since a null digest has no byte representation for Beam's serialization pipeline. This mirrors the Count-Min Sketch coder's null policy: nulls are programmer errors and are rejected loudly rather than silently encoded as empty.","triggerScenarios":"A null MergingDigest reaches Beam's coder layer — e.g. a CombineFn that emits/extracts a null accumulator, a DoFn outputting null, or an uninitialized state value being shuffled or stored with TDigestQuantiles' coder.","commonSituations":"GlobalWindows/GBK paths serializing digests after a combine produced null; StateSpec-backed digests read before first write; refactored pipelines where a null return replaced an empty digest.","solutions":["Return a fresh MergingDigest (TDigestQuantilesFn.createAccumulator()) instead of null wherever a digest may be absent.","Audit CombineFn.createAccumulator/mergeAccumulators for null returns.","Initialize state cells before first read in StateSpec usage.","Map nulls to empty digests before the coder boundary if third-party code may produce them."],"exampleFix":"// before\nreturn accumulator == null ? null : accumulator;\n\n// after\nreturn accumulator == null ? new MergingDigest(compression) : accumulator;","handlingStrategy":"validation","validationCode":"if (digest == null) {\n  digest = tdigestFn.createAccumulator(); // fresh empty MergingDigest\n}","typeGuard":"boolean hasDigest(MergingDigest d) { return d != null; }","tryCatchPattern":"try {\n  coded.apply(Combine.globally(tdigestFn));\n} catch (CoderException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"cannot encode a null T-Digest\")) {\n    throw new IllegalStateException(\"Null MergingDigest emitted — check createAccumulator/mergeAccumulators\", e);\n  }\n  throw e;\n}","preventionTips":["Ensure CombineFn accumulators are never null — initialize in createAccumulator().","Read StateSpec-backed digests only after a guaranteed write, or store an empty digest initially.","Unit-test combine/extractOutput paths with null-prone inputs."],"tags":["java","beam","coder","null","tdigest","serialization"],"backgroundTag":"null-argument","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"}