{"record":{"id":"68de681b39d2d6e7","repo":"apache/hadoop","slug":"cannot-mutate-read-only-channel","errorCode":null,"errorMessage":"Cannot mutate read-only channel","messagePattern":"Cannot mutate read-only channel","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageClientReadChannel.java","lineNumber":114,"sourceCode":"  @Override\n  public int read(ByteBuffer dst) throws IOException {\n    throwIfNotOpen();\n\n    // Don't try to read if the buffer has no space.\n    if (dst.remaining() == 0) {\n      return 0;\n    }\n    LOG.trace(\n        \"Reading {} bytes at {} position from '{}'\", dst.remaining(), currentPosition, resourceId);\n    if (currentPosition == objectSize) {\n      return -1;\n    }\n    return contentReadChannel.readContent(dst);\n  }\n\n  @Override\n  public int write(ByteBuffer src) throws IOException {\n    throw new UnsupportedOperationException(\"Cannot mutate read-only channel\");\n  }\n\n  @Override\n  public long position() throws IOException {\n    return currentPosition;\n  }\n\n  /**\n   * Sets this channel's position.\n   *\n   * <p>This method will throw an exception if {@code newPosition} is greater than object size,\n   * which contradicts {@link SeekableByteChannel#position(long) SeekableByteChannel} contract.\n   * TODO(user): decide if this needs to be fixed.\n   *\n   * @param newPosition the new position, counting the number of bytes from the beginning.\n   * @return this channel instance\n   * @throws FileNotFoundException if the underlying object does not exist.\n   * @throws IOException on IO error","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageClientReadChannel.java#L96-L132","documentation":"write(ByteBuffer) on GoogleCloudStorageClientReadChannel always throws UnsupportedOperationException. The class only implements it because SeekableByteChannel requires the method; the channel is strictly read-only and all reads are served through its internal ContentReadChannel. There is no code path that ever accepts a write.","triggerScenarios":"Calling write(...) on a channel obtained from gcs.open(...) / GoogleCloudStorageClientReadChannel.create — generic adapter code that uses one SeekableByteChannel for both directions, tests exercising the interface, or code ported from FileChannel.","commonSituations":"Utility copy routines that try both directions on the same channel; porting local-filesystem code where RandomAccessFile/FileChannel is writable; frameworks probing writability with a probe write.","solutions":["Write through a write channel from gcs.create(resourceId, createOptions) or the FileSystem create() API instead.","Guard shared/generic code: call write only on channels opened for writing (e.g. instanceof check against the write-channel type).","For in-place modification, read the object, modify bytes, and rewrite it as a new object (GCS objects are immutable)."],"exampleFix":"// before\ntry (SeekableByteChannel ch = gcs.open(itemId)) {\n  ch.write(buf); // UnsupportedOperationException\n}\n\n// after\ntry (WritableByteChannel out = gcs.create(dstId, CreateFileOptions.DEFAULT)) {\n  out.write(buf);\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isReadOnlyChannel(SeekableByteChannel ch) {\n  return ch instanceof GoogleCloudStorageClientReadChannel;\n}","tryCatchPattern":null,"preventionTips":["Keep read and write paths separate: open read channels for reads, gcs.create() write channels for writes.","In generic copy utilities, branch on instanceof rather than probing with write().","Remember GCS objects are immutable — design for read-then-rewrite, never in-place mutation."],"tags":["gcs","read-only","unsupported-operation","byte-channel","write"],"backgroundTag":"read-only-channel-mutation","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}