plandex-ai/plandex · error
error building validate loop: %v
Error message
error building validate loop: %v
What it means
This error wraps a panic recovered from the buildRace validation goroutine. When buildValidateLoop panics (nil dereference, index out of range, etc.), the deferred recover converts the panic value into an error and sends it on the error channel so the racing build can fail gracefully instead of crashing the process; runtime.Goexit then stops the goroutine to prevent a double-send.
Source
Thrown at app/server/model/plan/build_race.go:231
onInitialStream := func(chunk string, buffer string) bool {
if !startedFallbacks && strings.Contains(buffer, "<PlandexIncorrect/>") && strings.Contains(buffer, "<PlandexComments>") {
log.Printf("buildRace - detected incorrect marker, triggering whole file build")
comments := utils.GetXMLContent(buffer, "PlandexComments")
startFallbacks(comments)
}
// keep streaming
return false
}
fileState.builderRun.AutoApplyValidationStartedAt = time.Now()
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in buildRace validation loop: %v\n%s", r, debug.Stack())
sendErr(fmt.Errorf("error building validate loop: %v", r))
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
log.Printf("buildRace - starting validation loop")
validateResult, err := fileState.buildValidateLoop(buildCtx, buildValidateLoopParams{
originalFile: originalFile,
updated: updated,
proposedContent: proposedContent,
desc: desc,
reasons: reasons,
syntaxErrors: syntaxErrors,
initialPhaseOnStream: onInitialStream,
isInitial: true,
sessionId: sessionId,
})
fileState.builderRun.AutoApplyValidationFinishedAt = time.Now()View on GitHub (pinned to e2d772072e)
Solutions
- Read the panic stack in the server log ('panic in buildRace validation loop') to find the exact nil/panic site in buildValidateLoop
- Add nil/empty checks at the top of buildValidateLoop for proposedContent, updated, and originalFile before processing
- Ensure fileState and fileState.builderRun are fully initialized before calling buildRace
- Guard concurrent access to fileState fields shared with other build goroutines with a mutex or send immutable copies
Example fix
// before
lines := strings.Split(proposedContent, "\n")
edit := lines[editStart-1]
// after
lines := strings.Split(proposedContent, "\n")
if editStart < 1 || editStart > len(lines) {
return buildValidateLoopResult{}, fmt.Errorf("edit start %d out of range (file has %d lines)", editStart, len(lines))
}
edit := lines[editStart-1] Defensive patterns
Strategy: try-catch
Validate before calling
if proposedContent == "" || fileState == nil || fileState.builderRun == nil {
return fmt.Errorf("precondition failed: empty proposed content or uninitialized fileState")
} Type guard
func safeIndex(s string, i int) (byte, bool) {
if i < 0 || i >= len(s) { return 0, false }
return s[i], true
} Try / catch
// in the goroutine, wrap the whole loop body; the library already does this:
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in validation loop: %v\n%s", r, debug.Stack())
sendErr(fmt.Errorf("error building validate loop: %v", r))
runtime.Goexit()
}
}()
// ... buildValidateLoop ...
}() Prevention
- Nil-check all inputs (proposedContent, updated, originalFile) at the entry of buildValidateLoop
- Bounds-check line/column indices derived from edit markers before slicing content
- Avoid sharing mutable fileState fields across racing goroutines without synchronization
- Add panic-recovery + structured logging in every spawned build goroutine
When it happens
Trigger: A runtime panic inside fileState.buildValidateLoop while it validates/applies proposed edits — e.g. nil map/slice access on proposed content, out-of-range index on file lines, or a nil pointer on a builderRun/fileState field during auto-apply validation.
Common situations: Proposed content has unexpected structure (empty file, malformed markers), a tree-sitter parse returns nil nodes that are dereferenced, or concurrent mutation of fileState fields while the validation goroutine reads them.
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/d7a8a74512391af0.
Report an issue: GitHub.