{"record":{"id":"bc88972c9b252919","repo":"plandex-ai/plandex","slug":"error-unmarshalling-result-file-v","errorCode":null,"errorMessage":"error unmarshalling result file: %v","messagePattern":"error unmarshalling result file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/result_helpers.go","lineNumber":419,"sourceCode":"\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tlog.Printf(\"panic in GetPlanFileResults: %v\\n%s\", r, debug.Stack())\n\t\t\t\t\terrCh <- fmt.Errorf(\"panic in GetPlanFileResults: %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\n\t\t\tbytes, err := os.ReadFile(filepath.Join(resultsDir, file.Name()))\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\n\t\t\tresultCh <- &result\n\t\t}(file)\n\t}\n\n\tfor i := 0; i < len(files); i++ {\n\t\tselect {\n\t\tcase err := <-errCh:\n\t\t\treturn nil, fmt.Errorf(\"error reading result files: %v\", err)\n\t\tcase result := <-resultCh:\n\t\t\tresults = append(results, result)\n\t\t}\n\t}\n\n\tsort.Slice(results, func(i, j int) bool {\n\t\treturn results[i].CreatedAt.Before(results[j].CreatedAt)","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/result_helpers.go#L401-L437","documentation":"In GetPlanFileResults' worker goroutine, json.Unmarshal fails on a result file read from the results dir. The file exists but its bytes are not valid PlanFileResult JSON — truncation, corruption, or a concurrent partial write.","triggerScenarios":"Result JSON is truncated (partial write), hand-edited incorrectly, or a schema change made the stored JSON incompatible with the current PlanFileResult struct (e.g. string where number is expected).","commonSituations":"Process killed mid-MarshalIndent leaving half-written files; version mismatch between writer and reader of result files; NaN/Infinity tokens from custom serialization.","solutions":["Inspect the corrupt file (delete or fix it) — the wrapped %v names the JSON offset/field","Regenerate the result by re-running the plan","Guard against partial writes: write to a temp file and atomically rename on completion","Keep PlanFileResult fields tolerant (pointers/omitempty) across versions"],"exampleFix":"// before: non-atomic write can leave truncated JSON\njson.MarshalIndent(result, ...); os.WriteFile(path, bytes, 0644)\n// after: atomic write\nf, _ := os.CreateTemp(dir, \"result-*.tmp\")\njson.NewEncoder(f).Encode(result)\nf.Close()\nos.Rename(f.Name(), path)","handlingStrategy":"validation","validationCode":"b, err := os.ReadFile(path)\nif err == nil && !json.Valid(b) {\n    log.Printf(\"corrupt result file detected: %s\", path)\n}","typeGuard":null,"tryCatchPattern":"results, err := db.GetPlanFileResults(orgId, planId)\nif err != nil && strings.Contains(err.Error(), \"error unmarshalling result file\") {\n    // locate and quarantine the file named in the wrapped error\n    return err\n}","preventionTips":["Always write result JSON atomically (temp + rename)","Keep PlanFileResult forward-compatible (pointers, omitempty)","Validate stored JSON after any crash/recovery event"],"tags":["go","json","file-parsing"],"backgroundTag":"json-unmarshal-failed","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"}