{"record":{"id":"0752f7e2bdf2b11b","repo":"google/ExoPlayer","slug":"failed-to-render-output-buffer-to-surface-decoder","errorCode":null,"errorMessage":"Failed to render output buffer to surface: decoder is not initialized.","messagePattern":"Failed to render output buffer to surface: decoder is not initialized\\.","errorType":"exception","errorClass":"Gav1DecoderException","httpStatus":null,"severity":"error","filePath":"extensions/av1/src/main/java/com/google/android/exoplayer2/ext/av1/Libgav1VideoRenderer.java","lineNumber":167,"sourceCode":"  /** {@inheritDoc} */\n  @Override\n  protected final Gav1Decoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)\n      throws Gav1DecoderException {\n    TraceUtil.beginSection(\"createGav1Decoder\");\n    int initialInputBufferSize =\n        format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;\n    Gav1Decoder decoder =\n        new Gav1Decoder(numInputBuffers, numOutputBuffers, initialInputBufferSize, threads);\n    this.decoder = decoder;\n    TraceUtil.endSection();\n    return decoder;\n  }\n\n  @Override\n  protected void renderOutputBufferToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface)\n      throws Gav1DecoderException {\n    if (decoder == null) {\n      throw new Gav1DecoderException(\n          \"Failed to render output buffer to surface: decoder is not initialized.\");\n    }\n    decoder.renderToSurface(outputBuffer, surface);\n    outputBuffer.release();\n  }\n\n  @Override\n  protected void setDecoderOutputMode(@C.VideoOutputMode int outputMode) {\n    if (decoder != null) {\n      decoder.setOutputMode(outputMode);\n    }\n  }\n\n  @Override\n  protected DecoderReuseEvaluation canReuseDecoder(\n      String decoderName, Format oldFormat, Format newFormat) {\n    return new DecoderReuseEvaluation(\n        decoderName,","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/av1/src/main/java/com/google/android/exoplayer2/ext/av1/Libgav1VideoRenderer.java#L149-L185","documentation":"Gav1DecoderException thrown by Libgav1VideoRenderer.renderOutputBufferToSurface when its decoder field is null, i.e. renderOutputBufferToSurface was called before the renderer initialized the decoder (during init) or after it was released/reset. It is a state/lifecycle misuse guard on the renderer, not a decode failure.","triggerScenarios":"Calling renderOutputBufferToSurface from outside the normal playback pipeline before onEnabled/onInit completed; concurrently releasing the renderer while a render call is in flight; renderer.reset/disable set decoder = null between operations.","commonSituations":"Custom player code driving renderer methods directly on the wrong thread; races during stopPlayback where release nulls the decoder while a queued buffer is being rendered.","solutions":["Only invoke renderOutputBufferToSurface after the renderer's initialization has produced a decoder (i.e. within the normal renderer callback lifecycle)","Serialize renderer lifecycle operations (init/flush/release) on the playback thread so no render call overlaps release","Null-check the decoder in derived code before calling this protected method"],"exampleFix":"// before\nrenderer.renderOutputBufferToSurface(buffer, surface);\n\n// after: guard in subclasses / callers\nif (renderer != null && isRendererInitialized()) {\n  renderer.renderOutputBufferToSurface(buffer, surface);\n}","handlingStrategy":"validation","validationCode":"// In subclasses/callers of Libgav1VideoRenderer: confirm init ran before render\n// (decoder is created in createDecoder during renderer initialization)\nif (renderer.isInitialized() && !renderer.isReleased()) { // your own lifecycle flags\n  renderer.renderOutputBufferToSurface(buffer, surface);\n}","typeGuard":null,"tryCatchPattern":"try {\n  renderer.renderOutputBufferToSurface(outputBuffer, surface);\n} catch (Gav1DecoderException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"not initialized\")) {\n    // lifecycle race: renderer was released/reset mid-render; drop the buffer\n    outputBuffer.release();\n  } else {\n    throw e;\n  }\n}","preventionTips":["Never call renderer render methods outside the playback thread lifecycle (onEnabled -> onDisabled)","Serialize release with in-flight renders on the same thread","Treat 'decoder is not initialized' as a signal of a lifecycle race, not a decode bug"],"tags":["android","av1","renderer-lifecycle","api-misuse"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}