{"record":{"id":"ddc9ce8f2fd9f539","repo":"plandex-ai/plandex","slug":"panic-in-deletependingresultsforpaths-v-n-s","errorCode":null,"errorMessage":"panic in DeletePendingResultsForPaths: %v\\n%s","messagePattern":"panic in DeletePendingResultsForPaths: (.+?)\\\\n(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":860,"sourceCode":"\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn fmt.Errorf(\"error reading results dir: %v\", err)\n\t}\n\n\terrCh := make(chan error, len(files))\n\n\tfor _, file := range files {\n\t\tresultId := strings.TrimSuffix(file.Name(), \".json\")\n\n\t\tgo func(resultId string) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tlog.Printf(\"panic in DeletePendingResultsForPaths: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"panic in DeletePendingResultsForPaths: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\truntime.Goexit() // don't allow outer function to continue and double-send to channel\n\t\t\t\t}\n\t\t\t}()\n\t\t\tbytes, err := os.ReadFile(filepath.Join(resultsDir, resultId+\".json\"))\n\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error reading result file: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar result PlanFileResult\n\t\t\terr = json.Unmarshal(bytes, &result)\n\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error unmarshalling result file: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n","sourceCodeStart":842,"sourceCodeEnd":878,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L842-L878","documentation":"Like RejectAllResults, DeletePendingResultsForPaths spawns one goroutine per result file with a recover() defer. A panic in the goroutine is logged and converted into this error message with a stack trace, sent on errCh, and the goroutine exits via runtime.Goexit(). It means reading/deleting a pending result panicked.","triggerScenarios":"Nil dereference or unexpected type inside the goroutine — e.g. result.ToApi() returning something dereferenced unsafely in IsPending, malformed state after unmarshal, or a race on shared data while checking paths[result.Path].","commonSituations":"Result JSON missing required fields after manual edits or version skew (older schema files read by newer code); data races between concurrent apply/delete jobs on the same plan; bugs in PlanFileResult.ToApi exposed by edge-case files.","solutions":["Use the logged stack trace (embedded in the error string) to find the panicking line","Guard nil fields in PlanFileResult/ToApi/IsPending before dereferencing","Migrate or remove legacy-schema result files that break the current unmarshal expectations","Test DeletePendingResultsForPaths against corrupt and minimal result fixtures"],"exampleFix":"// before\nif result.ToApi().IsPending() && paths[result.Path] {\n// after\ntoApi := result.ToApi()\nif toApi != nil && toApi.IsPending() && paths[toApi.Path] {","handlingStrategy":"type-guard","validationCode":"var result PlanFileResult\nif err := json.Unmarshal(bytes, &result); err != nil {\n    errCh <- fmt.Errorf(\"skipping corrupt result %s: %v\", resultId, err)\n    return\n}\ntoApi := result.ToApi()\nif toApi == nil {\n    errCh <- nil\n    return\n}","typeGuard":"func validPlanResult(r *PlanFileResultApi) bool {\n    return r != nil && r.Path != \"\"\n}","tryCatchPattern":"go func(resultId string) {\n    defer func() {\n        if r := recover(); r != nil {\n            log.Printf(\"panic in DeletePendingResultsForPaths: %v\\n%s\", r, debug.Stack())\n            errCh <- fmt.Errorf(\"panic in DeletePendingResultsForPaths: %v\\n%s\", r, debug.Stack())\n            runtime.Goexit()\n        }\n    }()\n    _ = processResult(resultId)\n}(resultId)","preventionTips":["Nil-check ToApi() results before calling IsPending/Path","Version-stamp result JSON and migrate old schemas before processing","Run race-detector tests on concurrent delete paths","Fuzz unmarshal with truncated/minimal result files"],"tags":["go","panic","concurrency","goroutine"],"backgroundTag":"goroutine-panic-recovered","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}