{"record":{"id":"98e25f8dd2230dc8","repo":"wavetermdev/waveterm","slug":"error-getting-block-w","errorCode":null,"errorMessage":"error getting block: %w","messagePattern":"error getting block: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/blockcontroller/blockcontroller.go","lineNumber":162,"sourceCode":"\t\treturn\n\t}\n\tgo DestroyBlockController(blockId)\n}\n\n// Public API Functions\n\nfunc ResyncController(ctx context.Context, tabId string, blockId string, rtOpts *waveobj.RuntimeOpts, force bool) error {\n\tif tabId == \"\" || blockId == \"\" {\n\t\treturn fmt.Errorf(\"invalid tabId or blockId passed to ResyncController\")\n\t}\n\n\tmu := getBlockResyncMutex(blockId)\n\tmu.Lock()\n\tdefer mu.Unlock()\n\n\tblockData, err := wstore.DBMustGet[*waveobj.Block](ctx, blockId)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting block: %w\", err)\n\t}\n\n\tcontrollerName := blockData.Meta.GetString(waveobj.MetaKey_Controller, \"\")\n\tconnName := blockData.Meta.GetString(waveobj.MetaKey_Connection, \"\")\n\n\t// Get existing controller\n\texisting := getController(blockId)\n\n\t// Check for connection change FIRST - always destroy on conn change\n\tif existing != nil {\n\t\texistingConnName := existing.GetConnName()\n\t\tif existingConnName != connName {\n\t\t\tlog.Printf(\"stopping blockcontroller %s due to conn change (from %q to %q)\\n\", blockId, existingConnName, connName)\n\t\t\tDestroyBlockController(blockId)\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\texisting = nil\n\t\t}\n\t}","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/blockcontroller/blockcontroller.go#L144-L180","documentation":"ResyncController fetches the block record from wstore with DBMustGet before resolving its controller. If the block does not exist (or the DB read fails), the error is wrapped as 'error getting block: %w'. This means resync was requested for a blockId the store does not know about.","triggerScenarios":"Calling ResyncController with a blockId that has no row in the wstore — a deleted block, a stale ID from a previous session, or a fabricated/typo'd ID.","commonSituations":"Frontend resyncing a block after it was closed/deleted elsewhere; stale IDs persisted client-side across app restarts; race where block deletion completes before a pending resync executes.","solutions":["Verify the blockId exists via wstore.DBGet[*waveobj.Block] before resyncing","Remove or skip stale block references on the caller side (clear cached block lists on restart)","Handle block deletion events to drop pending resync requests","If the block should exist, check the wstore DB file integrity/location"],"exampleFix":"// before\nResyncController(ctx, tabId, staleBlockId, nil, false)\n// after\nif _, err := wstore.DBGet[*waveobj.Block](ctx, blockId); err != nil {\n    return // block gone; skip resync\n}\nResyncController(ctx, tabId, blockId, nil, false)","handlingStrategy":"validation","validationCode":"if _, err := wstore.DBGet[*waveobj.Block](ctx, blockId); err != nil {\n    // block missing: skip resync\n}","typeGuard":null,"tryCatchPattern":"if err := ResyncController(ctx, tabId, blockId, nil, false); err != nil {\n    var notFound bool\n    if strings.Contains(err.Error(), \"error getting block\") { notFound = true }\n    _ = notFound\n}","preventionTips":["Prune stale block references from frontend state on restart","Subscribe to block-deletion events and drop pending resyncs","Confirm blockId against wstore before resync calls"],"tags":["database","blockcontroller","stale-reference"],"backgroundTag":"record-not-found","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}