hashicorp/terraform · error
tag object values must be strings
Error message
tag object values must be strings
What it means
Returned by the cloud backend config parser (cloud/backend.go:508) when the `workspaces.tags` attribute is an object or map type but one of its values is not a string. The parser builds a map[string]string; if any value's cty type is not cty.String it appends this error and returns immediately, rejecting the backend configuration.
Solutions
- Make every value in the tags object a string literal, e.g. `{ Env = "prod" }`.
- If values come from variables, type them as string(string) or string-map (map(string)).
Example fix
# before
workspaces {
tags = { Env = 1, Region = "us" }
}
# after
workspaces {
tags = { Env = "1", Region = "us" }
} Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure every tag value is a string before writing the backend block.
func tagValuesAllStrings(m map[string]any) error {
for k, v := range m {
if _, ok := v.(string); !ok { return fmt.Errorf("tag object value for %q must be string", k) }
}
return nil
} Type guard
func tagMapIsStringMap(v cty.Value) bool {
if !(v.Type().IsObjectType() || v.Type().IsMapType()) { return false }
for _, val := range v.AsValueMap() {
if val.Type() != cty.String { return false }
}
return true
} Prevention
- Quote all tag values in the backend block.
- Type tag variables as map(string).
- Run terraform init/validate to surface parser errors early.
When it happens
Trigger: A cloud backend block with `workspaces { tags = { Env = 1 } }` (number value) or `{ Flag = true }` (bool value) — val.Type() != cty.String for that value at cloud/backend.go:508.
Common situations: Using a number/bool/list as a tag value instead of a string. Forgetting to wrap a tag value in quotes.
Related errors
- tag elements must be strings
- your version of Terraform Enterprise does not support…
- at most 1 action can be invoked per operation
- can't set both encryption_key and kms_encryption_key
- Cannot set both 'source' and 'content'
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/f5e3a7efa68c9b62.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloud/backend.go:508
ret.token = val.AsString()
log.Printf("[TRACE] cloud: found token in cloud config block")
}
// Grab any workspace/project info from the nested config object in one go,
// so it's easier to work with.
var name, project string
if workspaces := obj.GetAttr("workspaces"); !workspaces.IsNull() {
if val := workspaces.GetAttr("name"); !val.IsNull() {
name = val.AsString()
log.Printf("[TRACE] cloud: found workspace name %q in cloud config block", name)
}
if val := workspaces.GetAttr("tags"); !val.IsNull() {
log.Printf("[TRACE] tags is a %q type", val.Type().FriendlyName())
tagsAsMap := make(map[string]string)
if val.Type().IsObjectType() || val.Type().IsMapType() {
for k, v := range val.AsValueMap() {
if v.Type() != cty.String {
diags = diags.Append(errors.New("tag object values must be strings"))
return ret, diags
}
tagsAsMap[k] = v.AsString()
}
log.Printf("[TRACE] cloud: using tags %q from cloud config block", tagsAsMap)
ret.workspaceMapping.TagsAsMap = tagsAsMap
} else if val.Type().IsTupleType() || val.Type().IsSetType() {
var tagsAsSet []string
length := val.LengthInt()
if length > 0 {
it := val.ElementIterator()
for it.Next() {
_, v := it.Element()
if !v.Type().Equals(cty.String) {
diags = diags.Append(errors.New("tag elements must be strings"))
return ret, diags
}
if vs := v.AsString(); vs != "" {View on GitHub (pinned to d32a084675)