gohugoio/hugo · error

%s: invalid state: invalid param type %T for shortcode %q, e

Error message

%s: invalid state: invalid param type %T for shortcode %q, expected a map

What it means

Hugo's shortcode parser detected an internal state inconsistency while collecting named (key=value) parameters: the shortcode's accumulated params field was already assigned a non-map value (typically a positional []any slice from earlier positional args). The parser expected a map[string]any because the current token is a named parameter, but the prior params do not match that type. This indicates the shortcode invocation mixes named and positional parameter styles in a way the parser cannot reconcile.

Source

Thrown at hugolib/shortcode.go:690

			}
			sc.templ = templ
		case currItem.IsInlineShortcodeName():
			sc.name = currItem.ValStr(source)
			sc.isInline = true
		case currItem.IsShortcodeParam():
			if !pt.IsValueNext() {
				continue
			} else if pt.Peek().IsShortcodeParamVal() {
				// named params
				if sc.params == nil {
					params := make(map[string]any)
					params[currItem.ValStr(source)] = pt.Next().ValTyped(source)
					sc.params = params
				} else {
					if params, ok := sc.params.(map[string]any); ok {
						params[currItem.ValStr(source)] = pt.Next().ValTyped(source)
					} else {
						return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a map", errorPrefix, params, sc.name)
					}
				}
			} else {
				// positional params
				if sc.params == nil {
					var params []any
					params = append(params, currItem.ValTyped(source))
					sc.params = params
				} else {
					if params, ok := sc.params.([]any); ok {
						params = append(params, currItem.ValTyped(source))
						sc.params = params
					} else {
						return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a slice", errorPrefix, params, sc.name)
					}
				}
			}
		case currItem.IsDone():

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Audit the failing shortcode invocation and convert all parameters to one style: either all positional (space-separated values) or all named (key="value").
  2. Check the shortcode template definition (layouts/shortcodes/<name>.html) to confirm which style .Get expects, then match the invocation to it.
  3. If you need both, redesign the shortcode to accept a single style and derive the rest internally.
  4. Run hugo --renderToMemory or hugo server and read the error prefix (%s:) to find the exact file/line of the offending invocation.

Example fix

// before
{{< mysc foo bar="baz" >}}

// after (all named)
{{< mysc first="foo" bar="baz" >}}
// or (all positional)
{{< mysc foo baz >}}
Defensive patterns

Strategy: validation

Validate before calling

// Before rendering content, lint each shortcode invocation for mixed styles.
// (Conceptual Go check over parsed shortcode tokens.)
func validateShortcodeParams(tokens []scToken) error {
    seenNamed, seenPositional := false, false
    for _, t := range tokens {
        if t.isNamedParam()  { seenNamed = true }
        if t.isPositional()  { seenPositional = true }
        if seenNamed && seenPositional {
            return fmt.Errorf("shortcode %q mixes named and positional params", tokens[0].name)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: A shortcode call that interleaves positional and named arguments, e.g. {{< mysc positionalval key="x" >}}, where the first token was parsed as positional (setting sc.params to []any) and a subsequent key=value token is then treated as a named param. The mismatch at shortcode.go:687-690 returns this error.

Common situations: Authoring a shortcode and forgetting to keep a single parameter style; copy-pasting a shortcode invocation and partially editing it to add a key=value while leaving positional values; upgrading Hugo where tokenization rules around params changed.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/39ceb931a8110bde. Report an issue: GitHub.