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

  1. Verify the tabId exists first via wstore.DBGet[*waveobj.Tab](ctx, tabId) before calling UpdateTabName
  2. Check that the ID is actually a tab ID (e.g. re-derive it with DBFindTabForBlockId if starting from a block)
  3. Refresh stale tab lists from the store instead of caching IDs across close/delete operations
  4. 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

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


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/4ed659207313aa47. Report an issue: GitHub.