wavetermdev/waveterm · error

unable to get layout id for given tab id %s: %w

Error message

unable to get layout id for given tab id %s: %w

What it means

GetLayoutIdForTab loads the Tab and returns its LayoutState id. If the DBGet itself errors (storage failure, not a nil tab) it wraps it as "unable to get layout id for given tab id %s: %w". It identifies which tab id the caller supplied so the layout action queue can report a precise failure.

Source

Thrown at pkg/wcore/layout.go:77

		}},
	}
}

func GetNewTabLayout() PortableLayout {
	return PortableLayout{
		{IndexArr: []int{0}, BlockDef: &waveobj.BlockDef{
			Meta: waveobj.MetaMapType{
				waveobj.MetaKey_View:       "term",
				waveobj.MetaKey_Controller: "shell",
			},
		}, Focused: true},
	}
}

func GetLayoutIdForTab(ctx context.Context, tabId string) (string, error) {
	tabObj, err := wstore.DBGet[*waveobj.Tab](ctx, tabId)
	if err != nil {
		return "", fmt.Errorf("unable to get layout id for given tab id %s: %w", tabId, err)
	}
	return tabObj.LayoutState, nil
}

func QueueLayoutAction(ctx context.Context, layoutStateId string, actions ...waveobj.LayoutActionData) error {
	layoutStateObj, err := wstore.DBGet[*waveobj.LayoutState](ctx, layoutStateId)
	if err != nil {
		return fmt.Errorf("unable to get layout state for given id %s: %w", layoutStateId, err)
	}

	for i := range actions {
		if actions[i].ActionId == "" {
			actions[i].ActionId = uuid.New().String()
		}
	}

	if layoutStateObj.PendingBackendActions == nil {
		layoutStateObj.PendingBackendActions = &actions

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify the id is a valid tab OID (check its OType / prefix).
  2. Re-fetch the current tab list and use a live tab id.
  3. Inspect the wrapped cause for store/IO issues and retry after recovery.
  4. Use DBGet[*waveobj.Tab] directly first as a pre-check if unsure.

Example fix

// before
layoutId, err := wcore.GetLayoutIdForTab(ctx, someId) // someId is a workspace id
// after
tab, _ := wstore.DBGet[*waveobj.Tab](ctx, someId)
if tab == nil {
    return fmt.Errorf("not a tab: %s", someId)
}
layoutId, err := wcore.GetLayoutIdForTab(ctx, someId)
Defensive patterns

Strategy: validation

Validate before calling

oref, err := waveobj.ParseORef(tabId)
if err != nil || oref.OType != waveobj.OType_Tab {
    return fmt.Errorf("id %s is not a tab", tabId)
}

Type guard

func isTabId(id string) bool {
    oref := waveobj.ParseORefNoErr(id)
    return oref != nil && oref.OType == waveobj.OType_Tab
}

Try / catch

layoutId, err := wcore.GetLayoutIdForTab(ctx, tabId)
if err != nil {
    log.Printf("layout lookup failed for tab %s: %v", tabId, err)
    return err
}

Prevention

When it happens

Trigger: Calling GetLayoutIdForTab (directly or via QueueLayoutActionForTab) with a tab id whose DBGet fails: store read error, invalid OID format rejected by the update layer, or decode failure of the tab row.

Common situations: Passing a layout id or workspace id instead of a tab id; querying a tab after it was deleted; corrupted tab record in the local store.

Related errors


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