plandex-ai/plandex · error

error unmarshalling result file: %v

Error message

error unmarshalling result file: %v

What it means

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.

Source

Thrown at app/server/db/result_helpers.go:419

				if r := recover(); r != nil {
					log.Printf("panic in GetPlanFileResults: %v\n%s", r, debug.Stack())
					errCh <- fmt.Errorf("panic in GetPlanFileResults: %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, file.Name()))

			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
			}

			resultCh <- &result
		}(file)
	}

	for i := 0; i < len(files); i++ {
		select {
		case err := <-errCh:
			return nil, fmt.Errorf("error reading result files: %v", err)
		case result := <-resultCh:
			results = append(results, result)
		}
	}

	sort.Slice(results, func(i, j int) bool {
		return results[i].CreatedAt.Before(results[j].CreatedAt)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect the corrupt file (delete or fix it) — the wrapped %v names the JSON offset/field
  2. Regenerate the result by re-running the plan
  3. Guard against partial writes: write to a temp file and atomically rename on completion
  4. Keep PlanFileResult fields tolerant (pointers/omitempty) across versions

Example fix

// before: non-atomic write can leave truncated JSON
json.MarshalIndent(result, ...); os.WriteFile(path, bytes, 0644)
// after: atomic write
f, _ := os.CreateTemp(dir, "result-*.tmp")
json.NewEncoder(f).Encode(result)
f.Close()
os.Rename(f.Name(), path)
Defensive patterns

Strategy: validation

Validate before calling

b, err := os.ReadFile(path)
if err == nil && !json.Valid(b) {
    log.Printf("corrupt result file detected: %s", path)
}

Try / catch

results, err := db.GetPlanFileResults(orgId, planId)
if err != nil && strings.Contains(err.Error(), "error unmarshalling result file") {
    // locate and quarantine the file named in the wrapped error
    return err
}

Prevention

When it happens

Trigger: 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).

Common situations: Process killed mid-MarshalIndent leaving half-written files; version mismatch between writer and reader of result files; NaN/Infinity tokens from custom serialization.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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