hashicorp/packer · error

unhandled buildvar type: %T

Error message

unhandled buildvar type: %T

What it means

ConvertPluginConfigValueToHCLValue converts a plugin-provided build variable (a plain Go interface{}) into a cty.Value for HCL2 interpolation. Packer throws this error when the value's Go type is not one of the supported kinds (bool, string, uint8, float64, int64, uint64, []string, []uint8, []int64, []uint64), so it cannot be represented as an HCL value.

Source

Thrown at hcl2template/utils.go:186

			vals[i] = cty.NumberIntVal(ev)
		}
		if len(vals) == 0 {
			buildValue = cty.ListValEmpty(cty.Number)
		} else {
			buildValue = cty.ListVal(vals)
		}
	case []uint64:
		vals := make([]cty.Value, len(v))
		for i, ev := range v {
			vals[i] = cty.NumberUIntVal(ev)
		}
		if len(vals) == 0 {
			buildValue = cty.ListValEmpty(cty.Number)
		} else {
			buildValue = cty.ListVal(vals)
		}
	default:
		return cty.Value{}, fmt.Errorf("unhandled buildvar type: %T", v)
	}
	return buildValue, nil
}

// GetVarsByType walks through a hcl body, and gathers all the Traversals that
// have a root type matching one of the specified top-level labels.
//
// This will only work on finite, expanded, HCL bodies.
func GetVarsByType(block *hcl.Block, topLevelLabels ...string) []hcl.Traversal {
	var travs []hcl.Traversal

	switch body := block.Body.(type) {
	case *hclsyntax.Body:
		travs = getVarsByTypeForHCLSyntaxBody(body)
	default:
		attrs, _ := body.JustAttributes()
		for _, attr := range attrs {
			travs = append(travs, attr.Expr.Variables()...)

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Coerce the build variable to a supported type before passing it: convert Go int to int64, maps/structs to string (e.g. JSON-encode), and []interface{} to []string
  2. Fix the plugin/builder code that produces the variable so it emits one of the supported types
  3. If it's your code calling ConvertPluginConfigValueToHCLValue, pre-validate types with a type switch and convert unsupported ones yourself
  4. Check the Packer/SDK version: newer SDKs may type values differently; align the producer and consumer versions

Example fix

// before
buildVars["port"] = 8080 // Go int -> unhandled
// after
buildVars["port"] = int64(8080) // or strconv.Itoa: buildVars["port"] = "8080"
Defensive patterns

Strategy: type-guard

Validate before calling

func isSupportedBuildVarType(v interface{}) bool {
	switch v.(type) {
	case bool, string, uint8, float64, int64, uint64, []string, []uint8, []int64, []uint64:
		return true
	}
	return false
}

Type guard

func coerceBuildVar(v interface{}) interface{} {
	switch t := v.(type) {
	case int:
		return int64(t)
	case int32:
		return int64(t)
	case uint:
		return uint64(t)
	case map[string]interface{}:
		b, _ := json.Marshal(t)
		return string(b)
	default:
		return v
	}
}

Prevention

When it happens

Trigger: HCL2Prepare (on HCL2Provisioner or HCL2PostProcessor) iterates buildVars and calls this converter; a build variable of an unhandled type — e.g. int (plain, not int64), map[string]string, []interface{}, nil, or a struct — hits the default branch.

Common situations: A builder or plugin passes build variables with types outside the whitelist (e.g. Go int or a map) via the build's generated variables; a caller constructs buildVars map[string]interface{} manually with an int or nested value instead of the supported primitives/slices.

Related errors


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/b34a13d41815db5c. Report an issue: GitHub.