wavetermdev/waveterm · error
tab not found: %q
Error message
tab not found: %q
What it means
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.
Source
Thrown at pkg/wstore/wstore.go:47
cachedClientId = clientId
}
// in the main server, this will not return empty string
// it does return empty in wsh, but all wstore methods are invalid in wsh mode, so that shouldn't be an issue
func GetClientId() string {
clientIdLock.Lock()
defer clientIdLock.Unlock()
if wavebase.IsDevMode() && cachedClientId == "" {
panic("cachedClientId is empty")
}
return cachedClientId
}
func UpdateTabName(ctx context.Context, tabId, name string) error {
return WithTx(ctx, func(tx *TxWrap) error {
tab, _ := DBGet[*waveobj.Tab](tx.Context(), tabId)
if tab == nil {
return fmt.Errorf("tab not found: %q", tabId)
}
if tabId != "" {
tab.Name = name
DBUpdate(tx.Context(), tab)
}
return nil
})
}
func UpdateObjectMeta(ctx context.Context, oref waveobj.ORef, meta waveobj.MetaMapType, mergeSpecial bool) error {
return WithTx(ctx, func(tx *TxWrap) error {
if oref.IsEmpty() {
return fmt.Errorf("empty object reference")
}
obj, _ := DBGetORef(tx.Context(), oref)
if obj == nil {
return ErrNotFound
}View on GitHub (pinned to a4447c1563)
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
Example fix
// before
err := wstore.UpdateTabName(ctx, tabId, "New Name")
// after
tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if tab == nil {
return nil // tab already gone; skip rename
}
err := wstore.UpdateTabName(ctx, tabId, "New Name") Defensive patterns
Strategy: validation
Validate before calling
tab, _ := wstore.DBGet[*waveobj.Tab](ctx, tabId)
if tab == nil { return fmt.Errorf("skip rename: no tab %q", tabId) } Try / catch
if err := wstore.UpdateTabName(ctx, tabId, name); err != nil {
if strings.Contains(err.Error(), "tab not found") { return nil } // already gone
return err
} Prevention
- 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
When it happens
Trigger: Calling wstore.UpdateTabName(ctx, tabId, name) with a tabId that was deleted, never created, mistyped/truncated, or belongs to a different database.
Common situations: 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.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- ErrNotFound
- %s: no such file
- secret not found: %s
- chat not found: %s
- tool call with ID %s not found in chat %s
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/4ed659207313aa47.
Report an issue: GitHub.