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
- Check and propagate the error from New before using the decoder
- Ensure Release() is called only at final teardown and no frame requests happen afterwards
- Guard call sites to skip rendering when the decoder is nil
- 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
- Propagate New's error instead of discarding it
- Use ownership/locking so Release cannot run while frames are being rendered
- Nil-check the decoder at every render entry point
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
- failed to create decoder
- invalid resize dimensions
- unexpected nil response from GetAssertion
- unexpected nil response from MakeCredential
- xsession was terminated
AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02).
Data as JSON: /api/errors/8e9a8fa4b3272323.
Report an issue: GitHub.