hashicorp/terraform · error

resource has an unsupported mode

Error message

resource %s has an unsupported mode %s

What it means

Thrown by jsonplan.marshalResource while building the JSON plan: the switch over addr.Resource.Resource.Mode only maps addrs.ManagedResourceMode ('M') and addrs.DataResourceMode ('D'). ResourceMode also defines EphemeralResourceMode ('E'), ListResourceMode ('L'), and InvalidResourceMode (0), none of which have a JSON representation, so any of them lands in the default branch. The persistent JSON plan format is only defined for managed and data resources.

Solutions

  1. Upgrade Terraform to a release where the JSON plan serializer supports the resource mode you are using.
  2. If you are extending Terraform, add a case for the new mode (or filter it out upstream) in internal/command/jsonplan/plan.go before calling marshalResource.
  3. Report a bug at https://github.com/hashicorp/terraform/issues including the resource address and the mode string printed in the message.
  4. Regenerate the plan with the same Terraform binary that will display it to avoid cross-version plan files.

Example fix

// before
switch addr.Resource.Resource.Mode {
case addrs.ManagedResourceMode:
	r.Mode = jsonstate.ManagedResourceMode
case addrs.DataResourceMode:
	r.Mode = jsonstate.DataResourceMode
default:
	return r, fmt.Errorf("resource %s has an unsupported mode %s", r.Address, addr.Resource.Resource.Mode.String())
}

// after (extend the serializer for the new mode or skip it)
case addrs.EphemeralResourceMode:
	return r, fmt.Errorf("ephemeral resources are not represented in the JSON plan: %s", r.Address)
Defensive patterns

Strategy: validation

Validate before calling

// Before calling jsonplan marshalers, filter to the modes the JSON plan can represent.
func jsonSerializableMode(m addrs.ResourceMode) bool {
    switch m {
    case addrs.ManagedResourceMode, addrs.DataResourceMode:
        return true
    }
    return false
}

for _, rc := range changes {
    if !jsonSerializableMode(rc.Addr.Resource.Resource.Mode) {
        continue // ephemeral/list/invalid are not part of the JSON plan
    }
    // ... marshal rc
}

Type guard

func isJSONPlanSerializableResource(a addrs.ConfigResource) bool {
    switch a.Resource.Mode {
    case addrs.ManagedResourceMode, addrs.DataResourceMode:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: An ephemeral or list resource (or one with a zero/Invalid mode) reaches the plan-change marshaling path; a new ResourceMode constant is added to internal/addrs without a case here; a corrupted in-memory plan object has Mode==0.

Common situations: Running an internal/dev Terraform build where ephemeral/list resources are being plumbed through plan serialization before the JSON layer is updated; a Terraform regression that routes an unsupported mode into MarshalResourceChanges; replaying a plan file produced by a mismatched Terraform binary.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/7b070aef3da9cf98. Report an issue: GitHub.

Appendix: source

Thrown at internal/command/jsonplan/plan.go:611

	if key != nil {
		if key == addrs.WildcardKey {
			// The wildcard key should only be set for a deferred instance.
			r.IndexUnknown = true
		} else {
			value := key.Value()
			if r.Index, err = ctyjson.Marshal(value, value.Type()); err != nil {
				return r, err
			}
		}
	}

	switch addr.Resource.Resource.Mode {
	case addrs.ManagedResourceMode:
		r.Mode = jsonstate.ManagedResourceMode
	case addrs.DataResourceMode:
		r.Mode = jsonstate.DataResourceMode
	default:
		return r, fmt.Errorf("resource %s has an unsupported mode %s", r.Address, addr.Resource.Resource.Mode.String())
	}
	r.ModuleAddress = addr.Module.String()
	r.Name = addr.Resource.Resource.Name
	r.Type = addr.Resource.Resource.Type
	r.ProviderName = rc.ProviderAddr.Provider.String()

	switch rc.ActionReason {
	case plans.ResourceInstanceChangeNoReason:
		r.ActionReason = "" // will be omitted in output
	case plans.ResourceInstanceReplaceBecauseCannotUpdate:
		r.ActionReason = ResourceInstanceReplaceBecauseCannotUpdate
	case plans.ResourceInstanceReplaceBecauseTainted:
		r.ActionReason = ResourceInstanceReplaceBecauseTainted
	case plans.ResourceInstanceReplaceByRequest:
		r.ActionReason = ResourceInstanceReplaceByRequest
	case plans.ResourceInstanceReplaceByTriggers:
		r.ActionReason = ResourceInstanceReplaceByTriggers
	case plans.ResourceInstanceDeleteBecauseNoResourceConfig:

View on GitHub (pinned to d32a084675)