plandex-ai/plandex · error
panic in RejectPlanFiles: %v\n%s
Error message
panic in RejectPlanFiles: %v\n%s
What it means
RejectPlanFiles runs RejectPlanFile in a goroutine per file with a recover() guard. If a panic occurs (e.g. nil dereference in RejectPlanFile or GetPlanFileResults), it is converted to 'panic in RejectPlanFiles: %v\n%s' including the stack trace, sent on errCh, and the goroutine exits via runtime.Goexit.
Source
Thrown at app/server/db/result_helpers.go:914
for i := 0; i < len(files); i++ {
err := <-errCh
if err != nil {
return fmt.Errorf("error deleting pending results: %v", err)
}
}
return nil
}
func RejectPlanFiles(orgId, planId string, files []string, now time.Time) error {
errCh := make(chan error, len(files))
for _, file := range files {
go func(file string) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in RejectPlanFiles: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in RejectPlanFiles: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
err := RejectPlanFile(orgId, planId, file, now)
if err != nil {
errCh <- err
return
}
errCh <- nil
}(file)
}
for i := 0; i < len(files); i++ {
err := <-errCh
if err != nil {
return fmt.Errorf("error rejecting plan files: %v", err)View on GitHub (pinned to e2d772072e)
Solutions
- Read the stack trace in the message to locate the panicking function and fix the nil/invalid input
- Validate orgId, planId and file paths before calling RejectPlanFiles
- Fix the underlying panic source in RejectPlanFile/GetPlanFileResults (nil checks) so the recover guard is a last resort
- Convert panics in helpers to returned errors instead of panics
Example fix
// before
func getPlanResultsDir(orgId, planId string) string {
return filepath.Join(cfg.DataDir, orgId, planId, "results")
}
// after
func getPlanResultsDir(orgId, planId string) (string, error) {
if orgId == "" || planId == "" {
return "", fmt.Errorf("orgId and planId must be non-empty")
}
return filepath.Join(cfg.DataDir, orgId, planId, "results"), nil
} Defensive patterns
Strategy: validation
Validate before calling
func validateRejectInputs(orgId, planId string, files []string) error {
if orgId == "" || planId == "" { return fmt.Errorf("orgId/planId required") }
for _, f := range files {
if f == "" || strings.ContainsAny(f, "\x00") { return fmt.Errorf("invalid file path: %q", f) }
}
return nil
} Try / catch
err := RejectPlanFiles(orgId, planId, files, time.Now())
if err != nil && strings.HasPrefix(err.Error(), "panic in RejectPlanFiles") {
log.Printf("panic recovered during reject: %v", err) // includes stack trace for diagnosis
} Prevention
- Validate orgId/planId/file paths before calling RejectPlanFiles
- Keep the recover() + runtime.Goexit guard pattern in all goroutine fan-outs
- Write unit tests covering empty/odd inputs to RejectPlanFile to flush out panics
- Convert panics in shared helpers into returned errors
When it happens
Trigger: Any panic inside RejectPlanFile called from this fan-out: nil map/pointer dereference, index out of range, or unexpected nil from helpers like getPlanResultsDir or GetPlanFileResults for a malformed orgId/planId/file.
Common situations: Bad inputs (empty orgId/planId, path with weird characters) leading a helper to nil-deref; concurrent map access; library code panicking on unexpected result state.
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/46732bdb70f141cf.
Report an issue: GitHub.