plandex-ai/plandex · error
panic in DeletePendingResultsForPaths: %v\n%s
Error message
panic in DeletePendingResultsForPaths: %v\n%s
What it means
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.
Source
Thrown at app/server/db/result_helpers.go:860
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("error reading results dir: %v", err)
}
errCh := make(chan error, len(files))
for _, file := range files {
resultId := strings.TrimSuffix(file.Name(), ".json")
go func(resultId string) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in DeletePendingResultsForPaths: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in DeletePendingResultsForPaths: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
bytes, err := os.ReadFile(filepath.Join(resultsDir, resultId+".json"))
if err != nil {
errCh <- fmt.Errorf("error reading result file: %v", err)
return
}
var result PlanFileResult
err = json.Unmarshal(bytes, &result)
if err != nil {
errCh <- fmt.Errorf("error unmarshalling result file: %v", err)
return
}
View on GitHub (pinned to e2d772072e)
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
Example fix
// before
if result.ToApi().IsPending() && paths[result.Path] {
// after
toApi := result.ToApi()
if toApi != nil && toApi.IsPending() && paths[toApi.Path] { Defensive patterns
Strategy: type-guard
Validate before calling
var result PlanFileResult
if err := json.Unmarshal(bytes, &result); err != nil {
errCh <- fmt.Errorf("skipping corrupt result %s: %v", resultId, err)
return
}
toApi := result.ToApi()
if toApi == nil {
errCh <- nil
return
} Type guard
func validPlanResult(r *PlanFileResultApi) bool {
return r != nil && r.Path != ""
} Try / catch
go func(resultId string) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in DeletePendingResultsForPaths: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in DeletePendingResultsForPaths: %v\n%s", r, debug.Stack())
runtime.Goexit()
}
}()
_ = processResult(resultId)
}(resultId) Prevention
- 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
When it happens
Trigger: 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].
Common situations: 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.
Related errors
- panic in UpdateContexts: %v\n%s
- panic in GetPlanConvo: %v\n%s
- panic in gitRemoveIndexLockFileIfExists: %v %s
- panic in DeleteDraftPlans: %v %s
- panic in GetFullCurrentPlanStateParams: %v\n%s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/ddc9ce8f2fd9f539.
Report an issue: GitHub.