{"record":{"id":"4ed659207313aa47","repo":"wavetermdev/waveterm","slug":"tab-not-found-q-4ed659","errorCode":null,"errorMessage":"tab not found: %q","messagePattern":"tab not found: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wstore/wstore.go","lineNumber":47,"sourceCode":"\tcachedClientId = clientId\n}\n\n// in the main server, this will not return empty string\n// it does return empty in wsh, but all wstore methods are invalid in wsh mode, so that shouldn't be an issue\nfunc GetClientId() string {\n\tclientIdLock.Lock()\n\tdefer clientIdLock.Unlock()\n\tif wavebase.IsDevMode() && cachedClientId == \"\" {\n\t\tpanic(\"cachedClientId is empty\")\n\t}\n\treturn cachedClientId\n}\n\nfunc UpdateTabName(ctx context.Context, tabId, name string) error {\n\treturn WithTx(ctx, func(tx *TxWrap) error {\n\t\ttab, _ := DBGet[*waveobj.Tab](tx.Context(), tabId)\n\t\tif tab == nil {\n\t\t\treturn fmt.Errorf(\"tab not found: %q\", tabId)\n\t\t}\n\t\tif tabId != \"\" {\n\t\t\ttab.Name = name\n\t\t\tDBUpdate(tx.Context(), tab)\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc UpdateObjectMeta(ctx context.Context, oref waveobj.ORef, meta waveobj.MetaMapType, mergeSpecial bool) error {\n\treturn WithTx(ctx, func(tx *TxWrap) error {\n\t\tif oref.IsEmpty() {\n\t\t\treturn fmt.Errorf(\"empty object reference\")\n\t\t}\n\t\tobj, _ := DBGetORef(tx.Context(), oref)\n\t\tif obj == nil {\n\t\t\treturn ErrNotFound\n\t\t}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wstore/wstore.go#L29-L65","documentation":"UpdateTabName looks up the tab in the SQLite-backed wave object store inside a transaction before renaming it. If no Tab object exists for the given tabId, the write is aborted and this error is returned so the caller knows the identifier referenced a nonexistent (or already deleted) tab rather than silently doing nothing.","triggerScenarios":"Calling wstore.UpdateTabName(ctx, tabId, name) with a tabId that was deleted, never created, mistyped/truncated, or belongs to a different database.","commonSituations":"Stale tab IDs held by a frontend after the tab was closed by another window/client; passing a blockId or workspaceId instead of a tabId; race where the tab is removed between listing and renaming.","solutions":["Verify the tabId exists first via wstore.DBGet[*waveobj.Tab](ctx, tabId) before calling UpdateTabName","Check that the ID is actually a tab ID (e.g. re-derive it with DBFindTabForBlockId if starting from a block)","Refresh stale tab lists from the store instead of caching IDs across close/delete operations","Log the offending tabId and fall back to a no-op if the tab is already gone"],"exampleFix":"// before\nerr := wstore.UpdateTabName(ctx, tabId, \"New Name\")\n// after\ntab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId)\nif tab == nil {\n\treturn nil // tab already gone; skip rename\n}\nerr := wstore.UpdateTabName(ctx, tabId, \"New Name\")","handlingStrategy":"validation","validationCode":"tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId)\nif tab == nil { return fmt.Errorf(\"skip rename: no tab %q\", tabId) }","typeGuard":null,"tryCatchPattern":"if err := wstore.UpdateTabName(ctx, tabId, name); err != nil {\n\tif strings.Contains(err.Error(), \"tab not found\") { return nil } // already gone\n\treturn err\n}","preventionTips":["Always obtain tabIds from current store queries, not cached client state","Check tab existence before mutations","Distinguish block vs tab vs workspace IDs at call boundaries"],"tags":["sqlite","not-found","waveobj"],"backgroundTag":"record-not-found","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}