google/ExoPlayer · error · VpxDecoderException

Failed to render output buffer to surface: decoder is not in

Error message

Failed to render output buffer to surface: decoder is not initialized.

What it means

LibvpxVideoRenderer.renderOutputBufferToSurface was called while its cached decoder field is null, i.e. before a decoder was created by initDecoder (or after it was released). This path only runs in OUTPUT_MODE_SURFACE_YUV, where the renderer must push the decoded buffer into the VpxDecoder itself.

Source

Thrown at extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java:165

  @Override
  protected VpxDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
      throws VpxDecoderException {
    TraceUtil.beginSection("createVpxDecoder");
    int initialInputBufferSize =
        format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
    VpxDecoder decoder =
        new VpxDecoder(
            numInputBuffers, numOutputBuffers, initialInputBufferSize, cryptoConfig, threads);
    this.decoder = decoder;
    TraceUtil.endSection();
    return decoder;
  }

  @Override
  protected void renderOutputBufferToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface)
      throws VpxDecoderException {
    if (decoder == null) {
      throw new VpxDecoderException(
          "Failed to render output buffer to surface: decoder is not initialized.");
    }
    decoder.renderToSurface(outputBuffer, surface);
    outputBuffer.release();
  }

  @Override
  protected void setDecoderOutputMode(@C.VideoOutputMode int outputMode) {
    if (decoder != null) {
      decoder.setOutputMode(outputMode);
    }
  }

  @Override
  protected DecoderReuseEvaluation canReuseDecoder(
      String decoderName, Format oldFormat, Format newFormat) {
    return new DecoderReuseEvaluation(
        decoderName,

View on GitHub (pinned to dd430f7053)

Solutions

  1. Do not set OUTPUT_MODE_SURFACE_YUV before the renderer reports the decoder as initialized; use the default OUTPUT_MODE_YUV (decoder outputs buffers rendered by the renderer).
  2. If subclassing, guard calls with a null check on the decoder / ensure initDecoder completed before any render call.
  3. Upgrade the extension — modern versions prefer VideoDecoderOutputBufferRenderer and surface-YUV is rarely needed.

Example fix

// before
player.setVideoOutputMode(C.OUTPUT_MODE_SURFACE_YUV); // before decoder init completes

// after
// keep default OUTPUT_MODE_YUV, or guard:
if (decoder != null) {
  decoder.renderToSurface(outputBuffer, surface);
}
Defensive patterns

Strategy: validation

Validate before calling

// Inside a subclass, before rendering to surface:
if (decoder == null) { throw new IllegalStateException("render before initDecoder"); /* or drop the buffer */ }

Try / catch

catch (VpxDecoderException e) { log and re-prepare; surface mode cannot render without an initialized decoder }

Prevention

When it happens

Trigger: Player switches to OUTPUT_MODE_SURFACE_YUV and renders an output buffer before initDecoder(...) has run, or setDecoderOutputMode is invoked in a window where decoder is null but a buffer is still rendered to surface. Almost always an ordering bug inside a custom renderer pipeline or an unsupported mode being forced.

Common situations: Apps forcing videoSurfaceViewYuv output mode with the VP9 extension; races between renderer initialization and the first frame; subclassing LibvpxVideoRenderer and calling renderOutputBufferToSurface directly.

Related errors


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/d9c596b1999ad4a2. Report an issue: GitHub.