{"record":{"id":"67b112c6af50f3a0","repo":"wagoodman/dive","slug":"error-notifying-layer-change-listeners-w","errorCode":null,"errorMessage":"error notifying layer change listeners: %w","messagePattern":"error notifying layer change listeners: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/dive/cli/internal/ui/v1/view/layer.go","lineNumber":76,"sourceCode":"}\n\nfunc (v *Layer) AddLayerChangeListener(listener ...LayerChangeListener) {\n\tv.listeners = append(v.listeners, listener...)\n}\n\nfunc (v *Layer) notifyLayerChangeListeners() error {\n\tbottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop := v.vm.GetCompareIndexes()\n\tselection := viewmodel.LayerSelection{\n\t\tLayer:           v.CurrentLayer(),\n\t\tBottomTreeStart: bottomTreeStart,\n\t\tBottomTreeStop:  bottomTreeStop,\n\t\tTopTreeStart:    topTreeStart,\n\t\tTopTreeStop:     topTreeStop,\n\t}\n\tfor _, listener := range v.listeners {\n\t\terr := listener(selection)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error notifying layer change listeners: %w\", err)\n\t\t}\n\t}\n\t// this is hacky, and I do not like it\n\tif layerDetails, err := v.gui.View(\"layerDetails\"); err == nil {\n\t\tif err := layerDetails.SetCursor(0, 0); err != nil {\n\t\t\tv.logger.Debug(\"Couldn't set cursor to 0,0 for layerDetails\")\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (v *Layer) Name() string {\n\treturn v.name\n}\n\n// Setup initializes the UI concerns within the context of a global [gocui] view object.\nfunc (v *Layer) Setup(body *gocui.View, header *gocui.View) error {\n\tv.logger.Trace(\"Setup()\")","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/wagoodman/dive/blob/d6c691947f8fda635c952a17ee3b7555379d58f0/cmd/dive/cli/internal/ui/v1/view/layer.go#L58-L94","documentation":"Layer.notifyLayerChangeListeners broadcasts a LayerSelection (current layer plus compare index bounds) to registered listeners — mainly the filetree view, which responds by calling SetTree for the newly selected layer. If any listener returns an error, it is wrapped here. In practice the wrapped error is usually a filetree failure such as 'invalid layer index given' or 'unable to fetch layer tree from cache'.","triggerScenarios":"Changing the selected layer (arrow keys in the layer pane) causing a listener to fail: the filetree's SetTreeByLayer receiving out-of-range indexes, comparer cache fetch errors, or tree propagation/visit errors bubbling up through the listener chain.","commonSituations":"Navigating layers of an image whose analysis produced inconsistent RefTrees; selecting an aggregated view with edge-case layer counts (single-layer images); corrupted/unusual OCI layers that fail tree stacking when the selection changes.","solutions":["Read the wrapped error — it carries the real cause (usually filetree/comparer); fix that first","Re-run analysis on the image; transient tarball fetch issues during image load can produce broken ref trees","Try the other compare mode (layer vs aggregated) to see if the failure is mode-specific","Report upstream with the image reference and full wrapped error if reproducible"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// clamp selection to valid layer range before notifying listeners\nif selection.LayerIdx > len(refTrees)-1 { selection.LayerIdx = len(refTrees) - 1 }","typeGuard":null,"tryCatchPattern":"if err := v.notifyLayerChangeListeners(); err != nil {\n    // layer selection succeeded; degrade gracefully\n    v.logger.WithFields(\"error\", err).Warn(\"layer change listener failed\")\n}","preventionTips":["Validate compare indexes against len(RefTrees) before broadcasting","Make filetree listeners tolerant of per-layer tree failures","Re-analyze images whose layers fail tree building"],"tags":["ui","listeners","layer-selection","filetree"],"backgroundTag":null,"analyzedSha":"d6c691947f8fda635c952a17ee3b7555379d58f0","analyzedAt":"2026-08-15T09:42:35.293Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}