plandex-ai/plandex · error

plan updates out of order: %s

Error message

plan updates out of order: %s

What it means

While assembling files from ordered plan results, GetFilesBeforeReplacement detected a second (later) update for a path when content for that path had already been recorded and then the ordering check found non-empty prior content at an unexpected position — i.e. plan results for the same file arrive out of chronological order. The library fails fast because applying out-of-order updates would produce wrong file content.

Source

Thrown at app/shared/plan_result_replacements.go:190

			}

			if planRes.RemovedFile {
				updated = ""
				delete(files, path)
				delete(shas, path)
				delete(updatedAtByPath, path)
				removedByPath[path] = true
				continue
			}

			if len(planRes.Replacements) == 0 {
				if updated != "" {
					log.Println("plan updates out of order:", path)
					log.Println("updated:")
					log.Println(updated)
					log.Println("planRes.Content:")
					log.Println(planRes.Content)
					return nil, fmt.Errorf("plan updates out of order: %s", path)
				}

				updated = planRes.Content
				files[path] = updated
				updatedAtByPath[path] = planRes.CreatedAt
				delete(removedByPath, path)

				continue
			} else if updated == "" {
				context := planState.ContextsByPath[path]

				if context == nil {
					// spew.Dump(planRes)

					return nil, fmt.Errorf("no context for path: %s", path)
				}

				// log.Println("No updated content -- setting to context body")

View on GitHub (pinned to e2d772072e)

Solutions

  1. Ensure plan results are sorted by CreatedAt before calling GetFiles
  2. Deduplicate plan results per path, keeping only the latest entry
  3. Audit the writer that produced the out-of-order results (retries/concurrency) and serialize writes per path
  4. Use the logged `updated` vs `planRes.Content` output to identify the offending result and delete/repair it

Example fix

// before
files := GetFiles(planResults)
// after
sort.Slice(planResults, func(i, j int) bool { return planResults[i].CreatedAt.Before(planResults[j].CreatedAt) })
files := GetFiles(planResults)
Defensive patterns

Strategy: validation

Validate before calling

func ensureOrdered(results []shared.PlanResult) error {
    seen := map[string]time.Time{}
    for _, r := range results {
        if t, ok := seen[r.Path]; ok && r.CreatedAt.Before(t) {
            return fmt.Errorf("out-of-order result for %s", r.Path)
        }
        seen[r.Path] = r.CreatedAt
    }
    return nil
}

Try / catch

files, err := GetFiles(results, state)
if err != nil {
    if strings.Contains(err.Error(), "plan updates out of order") {
        path := extractPath(err.Error())
        results = filterLatestPerPath(results, path)
        files, err = GetFiles(results, state)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling GetFiles (which delegates to GetFilesBeforeReplacement) with a plan result list where a path's update content appears again after it was already set, with the logged `updated` differing from the current planRes.Content ordering assumptions — i.e. duplicate or reordered updates for the same path.

Common situations: Plan result storage or streaming that got replayed/reordered (retries, concurrent writers, manual DB edits); multiple plan runs writing the same file with stale results interleaved.

Related errors


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