getsops/sops · error

invalid %s key configuration: expected string in list, got %

Error message

invalid %s key configuration: expected string in list, got %T

What it means

parseKeyField accepts a string, []string, or nil for a key-group field; this error fires when a YAML list element is not a string (e.g. a map or number) while processing %s key configuration. It names the offending element's Go type to help locate the bad entry in the config file.

Source

Thrown at config/config.go:251

			return []string{}, nil
		}
		// Existing CSV parsing logic
		keys := strings.Split(v, ",")
		result := make([]string, 0, len(keys))
		for _, key := range keys {
			trimmed := strings.TrimSpace(key)
			if trimmed != "" { // Skip empty strings (fixes trailing comma issue)
				result = append(result, trimmed)
			}
		}
		return result, nil
	case []interface{}:
		result := make([]string, len(v))
		for i, item := range v {
			if str, ok := item.(string); ok {
				result[i] = str
			} else {
				return nil, fmt.Errorf("invalid %s key configuration: expected string in list, got %T", fieldName, item)
			}
		}
		return result, nil
	case []string:
		return v, nil
	default:
		return nil, fmt.Errorf("invalid %s key configuration: expected string, []string, or nil, got %T", fieldName, field)
	}
}

func NewStoresConfig() *StoresConfig {
	storesConfig := &StoresConfig{}
	storesConfig.JSON.Indent = -1
	storesConfig.JSONBinary.Indent = -1
	return storesConfig
}

// Load loads a sops config file into a temporary struct

View on GitHub (pinned to 13442bb981)

Solutions

  1. Make every list entry a plain quoted string (e.g. `- age1abc...`, `- arn:aws:kms:...` as a single string)
  2. Quote entries that YAML would parse as numbers or booleans
  3. Inspect the exact line indicated by the field name and %T in the message to find the non-string element

Example fix

# before
age:
  - recipient: age1abc...   # map, not string
# after
age:
  - "age1abc..."             # plain string entry
Defensive patterns

Strategy: validation

Validate before calling

// validate that all entries under a key field are plain strings
func isStringList(v interface{}) bool {
	switch l := v.(type) {
	case []interface{}:
		for _, item := range l { if _, ok := item.(string); !ok { return false } }
		return true
	case []string, string, nil:
		return true
	}
	return false
}

Type guard

func asStringList(field interface{}) ([]string, bool) {
	switch v := field.(type) {
	case string: return []string{v}, true
	case []string: return v, true
	case []interface{}:
		r := make([]string, 0, len(v))
		for _, it := range v {
			s, ok := it.(string); if !ok { return nil, false }
			r = append(r, s)
		}
		return r, true
	}
	return nil, false
}

Try / catch

keys, err := cfg.GetAgeKeys(creationRuleIndex)
if err != nil {
	if strings.Contains(err.Error(), "expected string in list") {
		return fmt.Errorf("fix .sops.yaml: every list entry must be a quoted plain string: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: A config list entry like `age: - {recipient: age1...}` or `- 123` is passed to parseKeyField via GetAgeKeys/GetKMSKeys/GetPGPKeys/etc., where an item is not a plain string.

Common situations: Copy-pasting KMS-style structured entries (arn maps) into an age/pgp list, YAML indentation accidentally nesting a mapping inside a string list, quoting mistakes turning entries into numbers or booleans (e.g. unquoted 12345).

Related errors


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/3e63bd7f4161bdf2. Report an issue: GitHub.