{"record":{"id":"587a77b71d76f73b","repo":"wavetermdev/waveterm","slug":"unable-to-update-layout-state-with-new-actions-w","errorCode":null,"errorMessage":"unable to update layout state with new actions: %w","messagePattern":"unable to update layout state with new actions: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wcore/layout.go","lineNumber":102,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to get layout state for given id %s: %w\", layoutStateId, err)\n\t}\n\n\tfor i := range actions {\n\t\tif actions[i].ActionId == \"\" {\n\t\t\tactions[i].ActionId = uuid.New().String()\n\t\t}\n\t}\n\n\tif layoutStateObj.PendingBackendActions == nil {\n\t\tlayoutStateObj.PendingBackendActions = &actions\n\t} else {\n\t\t*layoutStateObj.PendingBackendActions = append(*layoutStateObj.PendingBackendActions, actions...)\n\t}\n\n\terr = wstore.DBUpdate(ctx, layoutStateObj)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to update layout state with new actions: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc QueueLayoutActionForTab(ctx context.Context, tabId string, actions ...waveobj.LayoutActionData) error {\n\tlayoutStateId, err := GetLayoutIdForTab(ctx, tabId)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn QueueLayoutAction(ctx, layoutStateId, actions...)\n}\n\nfunc ApplyPortableLayout(ctx context.Context, tabId string, layout PortableLayout, recordTelemetry bool) error {\n\tactions := make([]waveobj.LayoutActionData, len(layout)+1)\n\tactions[0] = waveobj.LayoutActionData{ActionType: LayoutActionDataType_ClearTree}\n\tfor i := 0; i < len(layout); i++ {\n\t\tlayoutAction := layout[i]","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wcore/layout.go#L84-L120","documentation":"After appending the new LayoutActionData to PendingBackendActions, QueueLayoutAction persists the modified LayoutState via wstore.DBUpdate. This error wraps any failure of that update (write error, DB closed, serialization problem). The in-memory change is not committed.","triggerScenarios":"wstore.DBUpdate fails while saving a LayoutState after appending actions — disk full, DB IO error, context cancellation/timeout on ctx, or object validation failure during update.","commonSituations":"Disk full or read-only filesystem on the machine hosting the wave store; parent context already cancelled or timed out when QueueLayoutAction is called; transient DB errors during startup/shutdown.","solutions":["Inspect the wrapped %w error to identify the wstore.DBUpdate failure cause","Ensure the passed ctx is live (not cancelled/timed out) when queueing actions","Check disk space and write permissions on the wave store data directory","Retry QueueLayoutActionForTab; actions are idempotent-keyed by ActionId so a requeue is safe"],"exampleFix":"// before\nctx := context.Background() // never cancelled, but may outlive app\n// after\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\nerr := wcore.QueueLayoutActionForTab(ctx, tabId, action)","handlingStrategy":"try-catch","validationCode":"select {\ncase <-ctx.Done():\n    return ctx.Err()\ndefault:\n}\n// proceed with QueueLayoutAction using a live context","typeGuard":null,"tryCatchPattern":"if err := wcore.QueueLayoutActionForTab(ctx, tabId, actions...); err != nil {\n    if strings.Contains(err.Error(), \"unable to update layout state\") {\n        // retry with backoff; check disk space / permissions\n    }\n}","preventionTips":["Pass a live (non-cancelled) context with a sane timeout","Monitor disk space on the wave store volume","Requeue safely on failure — ActionIds are auto-generated per attempt"],"tags":["waveterm","wstore","layout","db-write"],"backgroundTag":"database-write-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}