opentofu/opentofu · error

invalid opManagedPerformDepose currentObj: %w

Error message

invalid opManagedPerformDepose currentObj: %w

What it means

While decoding an opManagedPerformDepose node, operand 0 (currentObj) did not reference a result of type *exec.ResourceInstanceObject — typically a Builder.ResourceInstancePrior result describing the object about to be deposed. The wrapped 'refers to X, but need Y' text comes from unmarshalGetPrevResultOf; nil slots coerce to NilResultRef without error, so this is a definite wrong-type reference in the serialized graph. Unmodified Marshal output cannot trigger it.

Source

Thrown at internal/engine/internal/execgraph/graph_unmarshal.go:254

	}
	deletePlan, err := unmarshalGetPrevResultOf[*exec.ManagedResourceObjectFinalPlan](prevResults, rawOperands[0])
	if err != nil {
		return nil, fmt.Errorf("invalid opManagedPrepareDepose deletePlan: %w", err)
	}
	deposedKey, err := unmarshalGetPrevResultOf[addrs.DeposedKey](prevResults, rawOperands[1])
	if err != nil {
		return nil, fmt.Errorf("invalid opManagedPrepareDepose deposedKey: %w", err)
	}
	return builder.ManagedPrepareDepose(deletePlan, deposedKey), nil
}

func unmarshalOpManagedPerformDepose(rawOperands []uint64, prevResults []AnyResultRef, builder *Builder) (AnyResultRef, error) {
	if len(rawOperands) != 3 {
		return nil, fmt.Errorf("wrong number of operands (%d) for opManagedPerformDepose", len(rawOperands))
	}
	currentObj, err := unmarshalGetPrevResultOf[*exec.ResourceInstanceObject](prevResults, rawOperands[0])
	if err != nil {
		return nil, fmt.Errorf("invalid opManagedPerformDepose currentObj: %w", err)
	}
	finalDeletePlan, err := unmarshalGetPrevResultOf[*exec.ManagedResourceObjectFinalPlan](prevResults, rawOperands[1])
	if err != nil {
		return nil, fmt.Errorf("invalid opManagedPerformDepose finalDeletePlan: %w", err)
	}
	waitFor, err := unmarshalGetPrevResultWaiter(prevResults, rawOperands[2])
	if err != nil {
		return nil, fmt.Errorf("invalid opManagedPerformDepose waitFor: %w", err)
	}
	return builder.ManagedPerformDepose(currentObj, finalDeletePlan, waitFor), nil
}

func unmarshalOpManagedDeposedMeta(rawOperands []uint64, prevResults []AnyResultRef, builder *Builder) (AnyResultRef, error) {
	if len(rawOperands) != 3 {
		return nil, fmt.Errorf("wrong number of operands (%d) for unmarshalOpManagedDeposedMeta", len(rawOperands))
	}
	instAddr, err := unmarshalGetPrevResultOf[addrs.AbsResourceInstance](prevResults, rawOperands[0])
	if err != nil {

View on GitHub (pinned to 3561785c48)

Solutions

  1. Regenerate the plan with the same binary and re-apply
  2. Re-create or checksum-verify the plan artifact
  3. In programmatic graphs, pass the Builder.ResourceInstancePrior result for the instance being deposed as ManagedPerformDepose's first argument
  4. Report an OpenTofu bug with the plan if the same version round-tripped it

Example fix

# before
deposeRef := builder.ManagedPerformDepose(addrRef, deletePlanRef, waiterRef) // currentObj slot holds an address ref

# after
currentObjRef := builder.ResourceInstancePrior(addrRef) // ResultRef[*exec.ResourceInstanceObject]
deposeRef := builder.ManagedPerformDepose(currentObjRef, deletePlanRef, waiterRef)
Defensive patterns

Strategy: try-catch

Try / catch

graph, err := execgraph.UnmarshalGraph(plan.ExecutionGraph)
if err != nil {
    return nil, fmt.Errorf("invalid execution graph in plan (opManagedPerformDepose currentObj operand): %w", err)
}

Prevention

When it happens

Trigger: UnmarshalGraph where opManagedPerformDepose's operands[0] points at an element of another type, e.g. an address constant or a final-plan result, due to operand-position shifts or corrupted bytes.

Common situations: Cross-version plan files exercising create-before-destroy; corrupted or re-serialized artifacts; forks rewiring the depose subgraph; hand-built ExecutionGraph bytes.

Related errors


AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15). Data as JSON: /api/errors/a670b944fe6c4917. Report an issue: GitHub.