gravitational/teleport · error

decoder not initialized

Error message

decoder not initialized

What it means

ResizeCrop in lib/srv/desktop/rdp/decoder/decoder.go returns 'decoder not initialized' when the receiver is nil or its native pointer d.ptr is nil, i.e. ResizeCrop is invoked on a Decoder that was never successfully created (New failed) or has already been Released.

Source

Thrown at lib/srv/desktop/rdp/decoder/decoder.go:169

	if data == nil || outWidth == 0 || outHeight == 0 {
		return nil
	}

	rgba := image.NewRGBA(image.Rect(0, 0, int(outWidth), int(outHeight)))

	// Copy from the Rust-owned memory into Go memory.
	copy(rgba.Pix, unsafe.Slice((*uint8)(data), int(outWidth)*int(outHeight)*4))

	return rgba
}

// ResizeCrop returns the source crop region (cropX, cropY, cropW, cropH) scaled to exactly outWidth x outHeight using
// high-quality CatmullRom convolution. The crop must lie within the current frame bounds. When withCursor is true and
// the decoder's tracked cursor is visible, it is composited onto the source frame before the crop is taken, so the
// cursor scales with the screen.
func (d *Decoder) ResizeCrop(cropX, cropY, cropW, cropH, outWidth, outHeight uint16, withCursor bool) (*image.RGBA, error) {
	if d == nil || d.ptr == nil {
		return nil, errors.New("decoder not initialized")
	}
	if outWidth == 0 || outHeight == 0 || cropW == 0 || cropH == 0 {
		return nil, errors.New("invalid resize dimensions")
	}

	bpp := int(C.rdp_decoder_bytes_per_pixel(d.ptr))
	if bpp == 0 {
		return nil, errors.New("decoder has no pixel format")
	}

	w, h := int(outWidth), int(outHeight)
	buf := make([]byte, w*h*bpp)

	var withCursorC C.uint8_t
	if withCursor {
		withCursorC = 1
	}

View on GitHub (pinned to 1283425b60)

Solutions

  1. Check and propagate the error from New before using the decoder
  2. Ensure Release() is called only at final teardown and no frame requests happen afterwards
  3. Guard call sites to skip rendering when the decoder is nil
  4. If using a shared decoder, add locking so release and resize don't race

Example fix

// before
dec, _ := rdpdecoder.New(w, h, cfg)
img, err := dec.ResizeCrop(x, y, cw, ch, ow, oh, false)
// after
dec, err := rdpdecoder.New(w, h, cfg)
if err != nil { return err }
img, err := dec.ResizeCrop(x, y, cw, ch, ow, oh, false)
Defensive patterns

Strategy: type-guard

Type guard

func canResize(d *rdpdecoder.Decoder) bool {
  return d != nil
}

Try / catch

img, err := dec.ResizeCrop(x, y, cw, ch, ow, oh, withCursor)
if err != nil {
  if strings.Contains(err.Error(), "decoder not initialized") {
    return nil, fmt.Errorf("decoder lifecycle bug: New failed or Release already called: %w", err)
  }
  return nil, err
}

Prevention

When it happens

Trigger: Calling ResizeCrop after New returned a nil decoder (creation failure propagated incorrectly), or after Release() freed the native handle, or on a nil *Decoder.

Common situations: Playback code that ignores the error from New and continues to render frames; double-release/after-release frame requests during session playback shutdown.

Related errors


AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02). Data as JSON: /api/errors/8e9a8fa4b3272323. Report an issue: GitHub.