{"record":{"id":"8e9a8fa4b3272323","repo":"gravitational/teleport","slug":"decoder-not-initialized","errorCode":null,"errorMessage":"decoder not initialized","messagePattern":"decoder not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/srv/desktop/rdp/decoder/decoder.go","lineNumber":169,"sourceCode":"\tif data == nil || outWidth == 0 || outHeight == 0 {\n\t\treturn nil\n\t}\n\n\trgba := image.NewRGBA(image.Rect(0, 0, int(outWidth), int(outHeight)))\n\n\t// Copy from the Rust-owned memory into Go memory.\n\tcopy(rgba.Pix, unsafe.Slice((*uint8)(data), int(outWidth)*int(outHeight)*4))\n\n\treturn rgba\n}\n\n// ResizeCrop returns the source crop region (cropX, cropY, cropW, cropH) scaled to exactly outWidth x outHeight using\n// high-quality CatmullRom convolution. The crop must lie within the current frame bounds. When withCursor is true and\n// the decoder's tracked cursor is visible, it is composited onto the source frame before the crop is taken, so the\n// cursor scales with the screen.\nfunc (d *Decoder) ResizeCrop(cropX, cropY, cropW, cropH, outWidth, outHeight uint16, withCursor bool) (*image.RGBA, error) {\n\tif d == nil || d.ptr == nil {\n\t\treturn nil, errors.New(\"decoder not initialized\")\n\t}\n\tif outWidth == 0 || outHeight == 0 || cropW == 0 || cropH == 0 {\n\t\treturn nil, errors.New(\"invalid resize dimensions\")\n\t}\n\n\tbpp := int(C.rdp_decoder_bytes_per_pixel(d.ptr))\n\tif bpp == 0 {\n\t\treturn nil, errors.New(\"decoder has no pixel format\")\n\t}\n\n\tw, h := int(outWidth), int(outHeight)\n\tbuf := make([]byte, w*h*bpp)\n\n\tvar withCursorC C.uint8_t\n\tif withCursor {\n\t\twithCursorC = 1\n\t}\n","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/srv/desktop/rdp/decoder/decoder.go#L151-L187","documentation":"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.","triggerScenarios":"Calling ResizeCrop after New returned a nil decoder (creation failure propagated incorrectly), or after Release() freed the native handle, or on a nil *Decoder.","commonSituations":"Playback code that ignores the error from New and continues to render frames; double-release/after-release frame requests during session playback shutdown.","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"],"exampleFix":"// before\ndec, _ := rdpdecoder.New(w, h, cfg)\nimg, err := dec.ResizeCrop(x, y, cw, ch, ow, oh, false)\n// after\ndec, err := rdpdecoder.New(w, h, cfg)\nif err != nil { return err }\nimg, err := dec.ResizeCrop(x, y, cw, ch, ow, oh, false)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func canResize(d *rdpdecoder.Decoder) bool {\n  return d != nil\n}","tryCatchPattern":"img, err := dec.ResizeCrop(x, y, cw, ch, ow, oh, withCursor)\nif err != nil {\n  if strings.Contains(err.Error(), \"decoder not initialized\") {\n    return nil, fmt.Errorf(\"decoder lifecycle bug: New failed or Release already called: %w\", err)\n  }\n  return nil, err\n}","preventionTips":["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"],"tags":["rdp","decoder","nil-pointer","desktop-access"],"backgroundTag":"decoder-not-initialized","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}