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

  1. Read the stack trace in the message to locate the panicking function and fix the nil/invalid input
  2. Validate orgId, planId and file paths before calling RejectPlanFiles
  3. Fix the underlying panic source in RejectPlanFile/GetPlanFileResults (nil checks) so the recover guard is a last resort
  4. 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

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


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/46732bdb70f141cf. Report an issue: GitHub.