plandex-ai/plandex · error

Error storing plan result: planRes is nil

Error message

Error storing plan result: planRes is nil

What it means

onFinishBuildFile is the terminal callback for a single-file plan build and expects a non-nil *db.PlanFileResult holding the LLM's constructed file content. When it is called with planRes == nil, Plandex sends "Error storing plan result: planRes is nil" as a 500 ApiError on StreamDoneCh and reports via notify. This is an internal invariant violation: some code path invoked the finish callback without a produced result.

Source

Thrown at app/server/model/plan/build_finish.go:188

	activeBuild := fileState.activeBuild

	activePlan := GetActivePlan(planId, branch)

	if activePlan == nil {
		log.Println("onFinishBuildFile - Active plan not found")
		return
	}

	filePath := fileState.filePath

	log.Printf("onFinishBuildFile: %s\n", filePath)

	if planRes == nil {
		log.Println("onFinishBuildFile - planRes is nil")
		go notify.NotifyErr(notify.SeverityError, fmt.Errorf("onFinishBuildFile: planRes is nil"))

		activePlan.StreamDoneCh <- &shared.ApiError{
			Type:   shared.ApiErrorTypeOther,
			Status: http.StatusInternalServerError,
			Msg:    "Error storing plan result: planRes is nil",
		}
		return
	}

	err := db.ExecRepoOperation(db.ExecRepoOperationParams{
		OrgId:       currentOrgId,
		UserId:      fileState.currentUserId,
		PlanId:      planId,
		Branch:      branch,
		PlanBuildId: build.Id,
		Scope:       db.LockScopeWrite,
		Ctx:         activePlan.Ctx,
		CancelFn:    activePlan.CancelFn,
		Reason:      "store plan result",
	}, func(repo *db.GitRepo) error {
		log.Println("Storing plan result", planRes.Path)

View on GitHub (pinned to e2d772072e)

Solutions

  1. Inspect server logs for the preceding "onFinishBuildFile - planRes is nil" line and the model stream outcome to find which caller produced the nil result
  2. Check whether the model response for that file was empty/truncated (API errors, token limits) and retry the plan operation
  3. Audit the calling path (buildFile / buildStructuredEdits) to ensure every branch constructs a *db.PlanFileResult before calling onFinishBuildFile
  4. Ensure the plan's configured model is returning valid file content (switch model or reduce file size if responses are cut off)

Example fix

// before
res, err := parsePlanFileResult(streamRes)
if err != nil {
	return err
}
state.onFinishBuildFile(res)
// after
res, err := parsePlanFileResult(streamRes)
if err != nil {
	return err
}
if res == nil {
	return fmt.Errorf("no plan file result produced for %s", filePath)
}
state.onFinishBuildFile(res)
Defensive patterns

Strategy: type-guard

Validate before calling

// guard before invoking the finish callback
if planRes == nil {
	return fmt.Errorf("buildFile(%s): model stream ended without a file result", filePath)
}

Type guard

func isValidPlanFileResult(res *db.PlanFileResult) bool {
	return res != nil && res.Path != ""
}

// at the call site:
if !isValidPlanFileResult(planRes) {
	// route to error handler instead of onFinishBuildFile
}

Try / catch

// in Go there is no try/catch; guard the callback input
if planRes == nil {
	fileState.onBuildFileError(fmt.Errorf("planRes is nil for %s", filePath))
	return
}

Prevention

When it happens

Trigger: buildFile or buildStructuredEdits reaching onFinishBuildFile with a nil planRes — e.g. the model stream ended without yielding a file result, an upstream code path forgot to assign the result after parsing the response, or a fallback/error path calls the finish handler directly without constructing a PlanFileResult.

Common situations: Model API responses that come back truncated or empty so parsing produces no result; new/untested plan modes (e.g. structured edits) that return nil on certain operation shapes; custom forks where the streaming callback was modified; races where the stream was canceled before the result was captured.

Related errors


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