gohugoio/hugo · error

permalinks: pattern must be a string, got %T

Error message

permalinks: pattern must be a string, got %T

What it means

In the new slice-based permalinks format, each entry's `pattern` key must hold a string. decodePermalinksSlice does a direct type assertion `patternVal.(string)` and errors with the actual Go type if the value is anything else. This is a strict check — no coercion of numbers, booleans, or nested maps is attempted.

Source

Thrown at resources/page/permalinks.go:552

func decodePermalinksSlice(ms []map[string]any) (PermalinksConfig, error) {
	var configs PermalinksConfig
	for _, m := range ms {
		m = hmaps.CleanConfigStringMap(m)
		var cfg PermalinkConfig

		if targetVal, ok := m["target"]; ok {
			if err := mapstructure.WeakDecode(targetVal, &cfg.Target); err != nil {
				return nil, fmt.Errorf("permalinks: failed to decode target: %w", err)
			}
			cfg.Target.Kind = strings.ToLower(cfg.Target.Kind)
			cfg.Target.Path = filepath.ToSlash(strings.ToLower(cfg.Target.Path))
		}

		if patternVal, ok := m["pattern"]; ok {
			cfg.Pattern, ok = patternVal.(string)
			if !ok {
				return nil, fmt.Errorf("permalinks: pattern must be a string, got %T", patternVal)
			}
		} else {
			return nil, fmt.Errorf("permalinks: missing pattern")
		}

		configs = append(configs, cfg)
	}

	return configs, nil
}

func decodePermalinksMap(m map[string]any) (PermalinksConfig, error) {
	var configs PermalinksConfig
	config := hmaps.CleanConfigStringMap(m)

	for k, v := range config {
		switch v := v.(type) {
		case string:

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Quote the pattern so it parses as a string: `pattern = "/:year/:slug/"`.
  2. Ensure each entry in the permalinks array has `pattern` as a plain string, with target options under `target` instead.

Example fix

# before
[[permalinks]]
pattern = 2024

# after
[[permalinks]]
pattern = "/2024/:slug/"
Defensive patterns

Strategy: type-guard

Validate before calling

// each permalink value must be a string:
// [permalinks.page]
// posts = "/:year/:month/:slug/"

Type guard

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

Prevention

When it happens

Trigger: An array-format permalinks entry where `pattern` is a non-string, e.g. `pattern = 2024`, `pattern = true`, an array, or an inline table; also YAML values that parse as numbers/dates instead of strings when unquoted.

Common situations: Unquoted YAML values that the parser types as int/bool/date; accidentally nesting a map under `pattern`; generating config from templates/scripts that emit typed values.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/1a62d30630e04a7e.json. Report an issue: GitHub ↗.