{"record":{"id":"3c7671f5b41bd41f","repo":"plandex-ai/plandex","slug":"panic-in-getplanapplies-v-n-s","errorCode":null,"errorMessage":"panic in GetPlanApplies: %v\\n%s","messagePattern":"panic in GetPlanApplies: (.+?)\\\\n(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":1051,"sourceCode":"\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"error reading applies dir: %v\", err)\n\t}\n\n\tplanApplies := []*PlanApply{}\n\tvar mu sync.Mutex\n\n\terrCh := make(chan error, len(files))\n\n\tfor _, file := range files {\n\t\tgo func(file os.DirEntry) {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tlog.Printf(\"panic in GetPlanApplies: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"panic in GetPlanApplies: %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(appliesDir, file.Name()))\n\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error reading apply file: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar apply PlanApply\n\t\t\terr = json.Unmarshal(bytes, &apply)\n\n\t\t\tif err != nil {\n\t\t\t\terrCh <- fmt.Errorf(\"error unmarshalling apply file: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n","sourceCodeStart":1033,"sourceCodeEnd":1069,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L1033-L1069","documentation":"Each goroutine in GetPlanApplies has a recover() deferred handler; if a goroutine panics while processing an apply file, the panic is logged with its stack and converted into this error sent on errCh, which GetPlanApplies then wraps. It indicates an unexpected runtime panic (e.g. nil dereference in future extensions of the loop body) rather than an expected I/O error.","triggerScenarios":"Any runtime panic inside the per-file goroutine — nil pointer dereference or slice-out-of-range while handling file contents. With the current body this is rare; it becomes relevant when the loop body is extended.","commonSituations":"Custom-modified builds of this function added processing logic that panics on unexpected file content; out-of-memory-style edge cases in JSON handling.","solutions":["Read the debug.Stack() printed to the log to find the panicking line.","Fix the panicking code to handle nil/unexpected file content defensively.","Check the apply files in the applies dir for malformed content that triggers the panic path."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"applies, err := GetPlanApplies(orgId, planId)\nif err != nil && strings.Contains(err.Error(), \"panic in GetPlanApplies\") {\n    // stack trace is in the server log; escalate with that context\n    return nil, fmt.Errorf(\"internal panic while listing applies, see server log: %w\", err)\n}","preventionTips":["Keep nil checks around every value extracted inside the per-file goroutine.","Test the applies loop with malformed and empty files to catch panic paths early.","Monitor server logs for panic stack traces and fix the root line they point to."],"tags":["go","panic","concurrency"],"backgroundTag":"panic-in-goroutine","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}