{"record":{"id":"f485592758e906ce","repo":"apache/druid","slug":"writable-channel-is-not-available-the-output-chan","errorCode":null,"errorMessage":"Writable channel is not available. The output channel might be marked as read-only, hence no writes are allowed.","messagePattern":"Writable channel is not available\\. The output channel might be marked as read-only, hence no writes are allowed\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/processor/PartitionedOutputChannel.java","lineNumber":91,"sourceCode":"      final MemoryAllocator frameMemoryAllocator,\n      final Supplier<PartitionedReadableFrameChannel> readableChannelSupplier\n  )\n  {\n    return new PartitionedOutputChannel(\n        Preconditions.checkNotNull(writableChannel, \"writableChannel\"),\n        Preconditions.checkNotNull(frameMemoryAllocator, \"frameMemoryAllocator\"),\n        readableChannelSupplier\n    );\n  }\n\n  /**\n   * Returns the writable channel of this pair. The producer writes to this channel. Throws ISE if the output channel is\n   * read only.\n   */\n  public synchronized WritableFrameChannel getWritableChannel()\n  {\n    if (writableChannel == null) {\n      throw new ISE(\"Writable channel is not available. The output channel might be marked as read-only,\"\n                    + \" hence no writes are allowed.\");\n    } else {\n      return writableChannel;\n    }\n  }\n\n  /**\n   * Returns the memory allocator for the writable channel. The producer uses this to generate frames for the channel.\n   * Throws ISE if the output channel is read only.\n   */\n  public synchronized MemoryAllocator getFrameMemoryAllocator()\n  {\n    if (frameMemoryAllocator == null) {\n      throw new ISE(\"Frame allocator is not available. The output channel might be marked as read-only,\"\n                    + \" hence memory allocator is not required.\");\n    } else {\n      return frameMemoryAllocator;\n    }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/processor/PartitionedOutputChannel.java#L73-L109","documentation":"PartitionedOutputChannel wraps a per-partition OutputChannel; when created read-only the writable channel is never assigned (null). getWritableChannel() then throws ISE, because a producer is attempting to write to a channel that is explicitly marked read-only. Callers listed (openPartitionedChannel, runMerger, writableFrameChannel) are internal paths that only fetch it when writing is expected.","triggerScenarios":"Constructing PartitionedOutputChannel with readOnly=true and later calling getWritableChannel(); sharing a read-only output channel across stages where one stage still tries to write frames to it.","commonSituations":"Query-stage wiring where an output is marked read-only (e.g. broadcast/read-only consumers) but a producer incorrectly targets it; tests reusing a read-only channel for writes; refactors that changed the readOnly flag without updating writers.","solutions":["Create the output channel with readOnly=false if producers must write to it.","Check writableFrameChannel()/readOnly state before writing and route writes to the correct non-read-only channel.","Ensure each partition's producer is wired to its own writable OutputChannel rather than a shared read-only one."],"exampleFix":"// before\nPartitionedOutputChannel out = new PartitionedOutputChannel(channels, readOnly);\nout.getWritableChannel().write(frame);\n// after\nPartitionedOutputChannel out = new PartitionedOutputChannel(channels, false);\nout.getWritableChannel().write(frame);","handlingStrategy":"try-catch","validationCode":"if (outputChannel.isReadOnly()) {\n  throw new IllegalStateException(\"Refusing to write to read-only output channel\");\n}","typeGuard":"boolean writable(PartitionedOutputChannel ch) { return !ch.isReadOnly(); }","tryCatchPattern":"try {\n  WritableFrameChannel w = outputChannel.getWritableChannel();\n} catch (IllegalStateException e) {\n  // route writes to a writable channel or re-create with readOnly=false\n}","preventionTips":["Keep producer and consumer channel wiring separate.","Audit readOnly flags when refactoring stage code.","Fetch the writable channel once at producer setup."],"tags":["java","illegal-state","frame-channel"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}