hashicorp/nomad · error

failed to decode key %q to string

Error message

failed to decode key %q to string

What it means

parseKeyringConfigs decodes keyring provider blocks into a map, then for each key listed in the provider's ExtraKeysHCL asserts the decoded value is a string before copying it into provider.Config. This error names the key whose decoded value was not a string (e.g. a number, bool, or list).

Source

Thrown at command/agent/config_parse.go:639

	}

	for idx, obj := range keyringBlocks.Items {
		provider := c.KEKProviders[idx]
		if len(provider.ExtraKeysHCL) == 0 {
			continue
		}

		provider.Config = map[string]string{}

		var m map[string]any
		if err := hcl.DecodeObject(&m, obj.Val); err != nil {
			return err
		}

		for _, extraKey := range provider.ExtraKeysHCL {
			val, ok := m[extraKey].(string)
			if !ok {
				return fmt.Errorf("failed to decode key %q to string", extraKey)
			}
			provider.Config[extraKey] = val
		}

		// clear the extra keys for these blocks because we've already handled
		// them and don't want them to bubble up to the caller
		provider.ExtraKeysHCL = nil
	}

	sort.Slice(c.KEKProviders, func(i, j int) bool {
		return c.KEKProviders[i].ID() < c.KEKProviders[j].ID()
	})

	return nil
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Quote the offending key's value so HCL decodes it as a string: `kms_key_id = "12345"`
  2. Check the key name in the error message (%q) and locate it in the keyring block
  3. Flatten lists to a comma-separated string if the provider expects one value
  4. Confirm against the provider docs which keys are extra keys requiring string values

Example fix

// before
keyring {
  provider = "awskms"
  kms_key_id = 12345
}

// after
keyring {
  provider = "awskms"
  kms_key_id = "12345"
}
Defensive patterns

Strategy: validation

Validate before calling

func assertKeyringExtraKeysAreStrings(keyring map[string]any, extraKeys []string) error {
	for _, k := range extraKeys {
		if v, present := keyring[k]; present {
			if _, ok := v.(string); !ok {
				return fmt.Errorf("keyring key %q must be a string, got %T", k, v)
			}
		}
	}
	return nil
}

Type guard

func isString(v any) bool {
	_, ok := v.(string)
	return ok
}

Try / catch

cfg, err := ParseConfigFile(path)
if err != nil {
	if strings.Contains(err.Error(), "failed to decode key") {
		return fmt.Errorf("keyring extra key must be a quoted string in %s: %w", path, err)
	}
	return err
}

Prevention

When it happens

Trigger: A keyring block containing an extra key (one handled specially by the provider, listed in ExtraKeysHCL) with a non-string HCL value, e.g. `region = ["us-east-1"]` or `kms_key_id = 12345`, reached via ParseConfigFile/LoadConfig.

Common situations: Unquoted numbers/booleans in keyring provider config (AWS region lists, key IDs); schema drift between vault versions where a key moved into ExtraKeysHCL and now must be a string; templating that interpolates non-string types.

Understand the failure class

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/ddd3c03e27723b95. Report an issue: GitHub.