{"record":{"id":"7e420846c00d0b9e","repo":"grpc/grpc-java","slug":"failure-reading-serialized-stream","errorCode":null,"errorMessage":"failure reading serialized stream","messagePattern":"failure reading serialized stream","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/io/grpc/Metadata.java","lineNumber":1048,"sourceCode":"     * @param value to serialize\n     * @return serialized version of value, or null if value cannot be transmitted.\n     */\n    byte[] toAsciiString(T value);\n\n    /**\n     * Parse a serialized metadata value from an ASCII string.\n     *\n     * @param serialized value of metadata to parse\n     * @return a parsed instance of type T\n     */\n    T parseAsciiString(byte[] serialized);\n  }\n\n  private static byte[] streamToBytes(InputStream stream) {\n    try {\n      return ByteStreams.toByteArray(stream);\n    } catch (IOException ioe) {\n      throw new RuntimeException(\"failure reading serialized stream\", ioe);\n    }\n  }\n}\n","sourceCodeStart":1030,"sourceCodeEnd":1052,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/api/src/main/java/io/grpc/Metadata.java#L1030-L1052","documentation":"Metadata serializes binary values by fully reading an InputStream into bytes (streamToBytes). If reading the stream throws an IOException — stream already consumed, closed, or I/O failure — it is rethrown as an unchecked RuntimeException('failure reading serialized stream'). It indicates corrupted or exhausted input backing a metadata value.","triggerScenarios":"Putting a metadata value whose InputStream has already been read/closed (available()==0 or stream at EOF), a custom stream throwing IOException mid-read, or reusing the same InputStream-backed value across multiple RPCs.","commonSituations":"Reusing a Metadata object or its binary-marshaller streams across retries; passing a stream from an already-drained resource; custom BinaryMarshaller producing streams that throw on second read.","solutions":["Supply a fresh InputStream (or byte[]) for each metadata value; never reuse a consumed stream","Cache the bytes yourself and use a ByteArrayInputStream per serialization","Review custom BinaryMarshaller.stream() implementations for double-read bugs","Catch RuntimeException around metadata construction to fail fast with context"],"exampleFix":"// before\nMetadata.Key<byte[]> key = Metadata.Key.of(\"bin\", Metadata.BINARY_BYTE_MARSHALLER);\nmetadata.put(key, sharedInputStream); // reused across calls\n// after\nmetadata.put(key, ByteStreams.toByteArray(freshSource())); // copy bytes once, byte[] marshaller input is fresh","handlingStrategy":"try-catch","validationCode":"byte[] bytes = ByteStreams.toByteArray(source); // read once, validate success before attaching to metadata\nif (bytes.length == 0 && requireNonEmpty) throw new IllegalArgumentException(\"empty metadata value\");","typeGuard":null,"tryCatchPattern":"try {\n  metadata.put(binaryKey, value);\n} catch (RuntimeException e) {\n  if (\"failure reading serialized stream\".equals(e.getMessage())) {\n    throw new IllegalStateException(\"Metadata stream was consumed or closed; supply a fresh stream per RPC\", e);\n  }\n  throw e;\n}","preventionTips":["Never reuse an InputStream-backed metadata value across RPCs or retries","Prefer byte[]/ByteArrayInputStream values for binary metadata","Copy the stream to bytes yourself if the value must be sent multiple times","Test custom BinaryMarshallers with repeated serialization"],"tags":["grpc","metadata","ioexception","stream","serialization"],"backgroundTag":"file-read-failed","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}