{"record":{"id":"91d19dc88bb5a7b9","repo":"plandex-ai/plandex","slug":"error-getting-contexts-or-convo-v","errorCode":null,"errorMessage":"error getting contexts or convo: %v","messagePattern":"error getting contexts or convo: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/plan_helpers.go","lineNumber":229,"sourceCode":"\t}()\n\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tlog.Printf(\"panic in SyncPlanTokens: %v\\n%s\", r, debug.Stack())\n\t\t\t\terrCh <- fmt.Errorf(\"panic in SyncPlanTokens: %v\\n%s\", r, debug.Stack())\n\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t}\n\t\t}()\n\t\tvar err error\n\t\tconvos, err = GetPlanConvo(orgId, planId)\n\t\terrCh <- err\n\t}()\n\n\tfor i := 0; i < 2; i++ {\n\t\terr := <-errCh\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting contexts or convo: %v\", err)\n\t\t}\n\t}\n\n\tcontextTokens := 0\n\tfor _, context := range contexts {\n\t\tcontextTokens += context.NumTokens\n\t}\n\n\tconvoTokens := 0\n\tfor _, msg := range convos {\n\t\tconvoTokens += msg.Tokens\n\t}\n\n\t_, err := Conn.Exec(\"UPDATE branches SET context_tokens = $1, convo_tokens = $2 WHERE plan_id = $3 AND name = $4\", contextTokens, convoTokens, planId, branch)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error updating plan tokens: %v\", err)\n\t}","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/plan_helpers.go#L211-L247","documentation":"SyncPlanTokens collects two results from its goroutines via a channel loop; if either goroutine reports an error (including the recovered panics from errors 452/453), this wrapper 'error getting contexts or convo' is returned. It means fetching the plan's contexts and/or conversation messages failed, so token totals cannot be computed and the branches row is left unsynced.","triggerScenarios":"Either GetPlanContexts(orgId, planId, false, false) or GetPlanConvo(orgId, planId) returns an error — bad/missing planId, DB connection failure, permission/row-not-found, or a panic recovered into an error inside either goroutine.","commonSituations":"Called from RewindPlanHandler on a plan that was deleted mid-request; stale planId after a rewind removed records; Postgres unreachable; org-scoped query returning rows that fail to scan after schema changes.","solutions":["Unwrap the inner %v error: sql.ErrNoRows means the planId/orgId pair is invalid — validate the plan exists first","Check DB connectivity and retry SyncPlanTokens if the inner error is transient (connection reset, context deadline)","If the inner message is 'panic in SyncPlanTokens', fix the panicking code path per the logged stack trace","Verify GetPlanContexts and GetPlanConvo handle the branch/plan state produced by rewind operations"],"exampleFix":"// before\nreturn fmt.Errorf(\"error getting contexts or convo: %v\", err)\n// after\nif errors.Is(err, sql.ErrNoRows) {\n    return fmt.Errorf(\"plan %s not found for org %s: %w\", planId, orgId, err)\n}\nreturn fmt.Errorf(\"error getting contexts or convo: %w\", err)","handlingStrategy":"retry","validationCode":"var plan Plan\nif err := Conn.Get(&plan, \"SELECT id FROM plans WHERE id = $1 AND org_id = $2\", planId, orgId); err != nil {\n    return fmt.Errorf(\"plan not found for org: %w\", err)\n}","typeGuard":"func planAccessible(orgId, planId string) bool {\n    var exists bool\n    _ = Conn.Get(&exists, \"SELECT EXISTS(SELECT 1 FROM plans WHERE id = $1 AND org_id = $2)\", planId, orgId)\n    return exists\n}","tryCatchPattern":"for attempt := 0; attempt < 2; attempt++ {\n    err := SyncPlanTokens(orgId, planId, branch)\n    if err == nil {\n        break\n    }\n    if !errors.Is(err, sql.ErrNoRows) && attempt == 0 {\n        time.Sleep(500 * time.Millisecond)\n        continue\n    }\n    return err\n}","preventionTips":["Verify the plan exists and belongs to the org before rewinding/syncing","Retry transient DB errors (connection reset, deadline) with backoff","Fix any panic root-causes logged as 'panic in SyncPlanTokens'","Ensure rewind handlers do not delete plan data before the final token sync"],"tags":["database","go","concurrency"],"backgroundTag":"database-query-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}