hashicorp/terraform · error
cannot decode %s from flatmap
Error message
cannot decode %s from flatmap
What it means
Returned by hcl2ValueFromFlatmapValue (flatmap.go:176) when decoding a flatmap (the legacy string-keyed map representation of Terraform state) into a cty.Value whose type does not match any recognized kind. The switch covers primitive, object, tuple, map, list, and set; reaching the default means the cty.Type is something none of those checks recognize — typically a capsule type, a dynamically-typed value, or an internally-inconsistent type descriptor.
Source
Thrown at internal/configs/hcl2shim/flatmap.go:176
func hcl2ValueFromFlatmapValue(m map[string]string, key string, ty cty.Type) (cty.Value, error) {
var val cty.Value
var err error
switch {
case ty.IsPrimitiveType():
val, err = hcl2ValueFromFlatmapPrimitive(m, key, ty)
case ty.IsObjectType():
val, err = hcl2ValueFromFlatmapObject(m, key+".", ty.AttributeTypes())
case ty.IsTupleType():
val, err = hcl2ValueFromFlatmapTuple(m, key+".", ty.TupleElementTypes())
case ty.IsMapType():
val, err = hcl2ValueFromFlatmapMap(m, key+".", ty)
case ty.IsListType():
val, err = hcl2ValueFromFlatmapList(m, key+".", ty)
case ty.IsSetType():
val, err = hcl2ValueFromFlatmapSet(m, key+".", ty)
default:
err = fmt.Errorf("cannot decode %s from flatmap", ty.FriendlyName())
}
if err != nil {
return cty.DynamicVal, err
}
return val, nil
}
func hcl2ValueFromFlatmapPrimitive(m map[string]string, key string, ty cty.Type) (cty.Value, error) {
rawVal, exists := m[key]
if !exists {
return cty.NullVal(ty), nil
}
if rawVal == UnknownVariableValue {
return cty.UnknownVal(ty), nil
}
var err errorView on GitHub (pinned to c9def3e214)
Solutions
- Print ty.FriendlyName() (it appears in the message) and confirm whether that type is supposed to be representable in a flatmap — only primitive/object/tuple/map/list/set are supported.
- If the type is cty.DynamicPseudoType, resolve it to a concrete type before calling HCL2ValueFromFlatmap; flatmap cannot encode fully-dynamic values.
- Ensure the cty.Type used for decoding matches the type that originally produced the flatmap (type mismatch between schema versions causes this).
- If you have a custom capsule type, decode it through a dedicated path instead of the generic flatmap shim.
Example fix
// before val, err := hcl2shim.HCL2ValueFromFlatmap(m, key, myCapsuleType) // after — flatmap only supports the 6 standard kinds; use a concrete type val, err := hcl2shim.HCL2ValueFromFlatmap(m, key, cty.Map(cty.String))
Defensive patterns
Strategy: validation
Validate before calling
// Only the 6 standard cty kinds are decodable from a flatmap
func isFlatmapDecodable(ty cty.Type) bool {
return ty.IsPrimitiveType() || ty.IsObjectType() || ty.IsTupleType() ||
ty.IsMapType() || ty.IsListType() || ty.IsSetType()
}
// Resolve dynamic types and reject unsupported kinds BEFORE decoding:
if !isFlatmapDecodable(ty) {
return cty.DynamicVal, fmt.Errorf("flatmap cannot decode type %s", ty.FriendlyName())
} Type guard
func isFlatmapDecodable(ty cty.Type) bool {
return ty.IsPrimitiveType() || ty.IsObjectType() || ty.IsTupleType() ||
ty.IsMapType() || ty.IsListType() || ty.IsSetType()
} Try / catch
val, err := hcl2shim.HCL2ValueFromFlatmap(m, key, ty)
if err != nil {
// fall back to a null of the type rather than propagating a decode failure
log.Printf("flatmap decode failed for %s: %v", ty.FriendlyName(), err)
return cty.NullVal(ty), nil
} Prevention
- Never pass cty.DynamicPseudoType or capsule types into the flatmap round-trip without resolving them first.
- Keep the cty.Type used for decoding identical to the one that produced the flatmap.
- Validate schemas with InternalValidate to catch exotic types before they reach state I/O.
When it happens
Trigger: Calling HCL2ValueFromFlatmap with a cty.Type that is not one of the six handled kinds (e.g. a cty.Capsule type, or a type produced by cty.DynamicPseudoType that was not resolved before decoding). Also reachable when a custom/extended cty type leaks into the flatmap round-trip path.
Common situations: State migration or import code that passes a schema type containing dynamic pseudo-types without first resolving them. Provider schemas that declare attributes with unsupported underlying types. Hand-edited or third-party-tampered state files paired with a schema that doesn't match.
Related errors
- invalid value for %q in state: %s
- invalid count value for %q in state: %s
- wrong number of values for %q in state: got %d, but need %d
- unrecognized type: %s
- missing field in set: %s.%s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/f0d758a60ea5c3c7.
Report an issue: GitHub.