getsops/sops · error

invalid %s key configuration: expected string, []string, or

Error message

invalid %s key configuration: expected string, []string, or nil, got %T

What it means

parseKeyField's default branch: the field's overall value is neither a string, a []string, nor nil (e.g. a single map or nested list), so key parsing cannot proceed. Unlike error 66 (bad element inside a list), this fires on the whole field having an unsupported type.

Source

Thrown at config/config.go:258

			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
func (f *configFile) load(bytes []byte) error {
	err := yaml.Unmarshal(bytes, f)
	if err != nil {
		return fmt.Errorf("Could not unmarshal config file: %s", err)
	}
	return nil
}

View on GitHub (pinned to 13442bb981)

Solutions

  1. Set the field to either a single string or a list of strings in .sops.yaml
  2. For multiple key groups, use the key_groups structure instead of nesting lists on the key field
  3. Validate the YAML structure with `sops` dry-run or a schema check after editing

Example fix

# before
age:
  key1: age1abc...   # map not accepted
# after
age:
  - "age1abc..."
  - "age1def..."
Defensive patterns

Strategy: validation

Validate before calling

// field must be string, []string, or nil
func keyFieldOk(v interface{}) bool {
	switch v.(type) {
	case string, []string, []interface{}, nil:
		// []interface{} still needs element checks (see error 66)
		return true
	}
	return false
}

Type guard

func isScalarOrStringList(v interface{}) bool {
	if v == nil { return true }
	if s, ok := v.(string); ok { return s != "" }
	if l, ok := v.([]interface{}); ok { return len(l) > 0 }
	return false
}

Try / catch

keys, err := cfg.GetPGPKeys(i)
if err != nil && strings.Contains(err.Error(), "expected string, []string, or nil") {
	return fmt.Errorf(".sops.yaml field must be a string or list of strings, not a map: %w", err)
}

Prevention

When it happens

Trigger: Config like `age: {key1: val}` (a map where a string or list is expected), or a nested list `[[a, b]]` passed to GetAgeKeys/GetPGPKeys/GetKMSKeys etc.

Common situations: Confusing per-key creation-rule syntax with key-group syntax in .sops.yaml, pasting structured key objects into fields that sops expects as plain strings, wrong YAML indentation creating a mapping instead of a list of strings.

Related errors


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