plandex-ai/plandex · error

error building validate: %v

Error message

error building validate: %v

What it means

buildValidateLoop wraps any non-cancellation error from fileState.buildValidate into "error building validate: %v" and aborts the whole validate-and-fix loop, returning the error to its caller. It means the per-attempt LLM validation/repair step failed outright (as opposed to validation merely reporting the file invalid). Context cancellation is special-cased and propagated unwrapped.

Source

Thrown at app/server/model/plan/build_validate_and_fix.go:131

			syntaxErrors:    syntaxErrors,
			reasons:         reasons,
			modelConfig:     &modelConfig,
			validateOnly:    isLastAttempt && params.validateOnlyOnFinalAttempt,
			phase:           currentAttempt,
			isInitial:       params.isInitial,
			sessionId:       params.sessionId,
		}

		log.Printf("Calling buildValidate for attempt %d", currentAttempt)
		res, err := fileState.buildValidate(ctx, validateParams)
		if err != nil {
			if errors.Is(err, context.Canceled) {
				log.Printf("Context canceled during buildValidate")
				return buildValidateLoopResult{}, err
			}

			log.Printf("Error in buildValidate during attempt %d: %v", currentAttempt, err)
			return buildValidateLoopResult{}, fmt.Errorf("error building validate: %v", err)
		}
		updated = res.updated

		syntaxErrors = fileState.validateSyntax(ctx, updated)
		log.Printf("Found %d syntax errors after attempt %d", len(syntaxErrors), currentAttempt)

		if res.valid && len(syntaxErrors) == 0 {
			log.Printf("Validation succeeded in attempt %d", currentAttempt)
			return buildValidateLoopResult{
				valid:   res.valid,
				updated: res.updated,
			}, nil
		}

		problems = append(problems, res.problem)

		log.Printf("Validation failed in attempt %d, preparing for next attempt", currentAttempt)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the wrapped inner error to find the root cause (API vs parsing vs diff).
  2. Retry the plan build — the loop already switches to a stronger model after attempt 2, so transient provider errors may resolve on a fresh run.
  3. If the inner error is context.Canceled wrapped deeper, ensure errors.Is/As unwrapping is used so cancellation isn't reported as a build failure.
  4. Verify model credentials and quota before starting long validate/fix loops.

Example fix

// before
res, err := fileState.buildValidate(ctx, validateParams)
if err != nil {
    if errors.Is(err, context.Canceled) { return buildValidateLoopResult{}, err }
    return buildValidateLoopResult{}, fmt.Errorf("error building validate: %v", err)
}
// after
res, err := fileState.buildValidate(ctx, validateParams)
if err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        return buildValidateLoopResult{}, err // propagate, don't wrap
    }
    return buildValidateLoopResult{}, fmt.Errorf("error building validate (attempt %d): %w", currentAttempt, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight provider check before the validate loop
if err := clients.Ping(ctx); err != nil {
    return buildValidateLoopResult{}, fmt.Errorf("provider unavailable: %w", err)
}

Type guard

func isCancellation(err error) bool {
    return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
}

Try / catch

res, err := fileState.buildValidate(ctx, validateParams)
if err != nil {
    if isCancellation(err) {
        return buildValidateLoopResult{}, err
    }
    return buildValidateLoopResult{}, fmt.Errorf("error building validate (attempt %d): %w", currentAttempt, err)
}

Prevention

When it happens

Trigger: fileState.buildValidate(ctx, validateParams) returns a non-nil error that is not context.Canceled — e.g. the LLM validation call failed (network, auth, rate limit), XML response parsing returned an error like the handleXMLResponse failures, or diff computation inside buildValidate failed.

Common situations: Provider 429/5xx during the validation attempt, malformed model XML triggering the 'no old content found for replacement' path, expired credentials, or a transient network drop mid-stream.

Related errors


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