gohugoio/hugo · error
malformed character constant: %s
Error message
malformed character constant: %s
What it means
The Go template parser rejects malformed rune/character constants. For an itemCharConstant it calls strconv.UnquoteChar and requires the remaining tail to be a closing single quote; any leftover text is a malformed character constant. See node.go:636-643.
Source
Thrown at tpl/internal/go_templates/texttemplate/parse/node.go:642
IsFloat bool // Number has a floating-point value.
IsComplex bool // Number is complex.
Int64 int64 // The signed integer value.
Uint64 uint64 // The unsigned integer value.
Float64 float64 // The floating-point value.
Complex128 complex128 // The complex value.
Text string // The original textual representation from the input.
}
func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
switch typ {
case itemCharConstant:
rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
if err != nil {
return nil, err
}
if tail != "'" {
return nil, fmt.Errorf("malformed character constant: %s", text)
}
n.Int64 = int64(rune)
n.IsInt = true
n.Uint64 = uint64(rune)
n.IsUint = true
n.Float64 = float64(rune) // odd but those are the rules.
n.IsFloat = true
return n, nil
case itemComplex:
// fmt.Sscan can parse the pair, so let it do the work.
if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
return nil, err
}
n.IsComplex = true
n.simplifyComplex()
return n, nil
}
// Imaginary constants can only be complex unless they are zero.View on GitHub (pinned to 52c9bd7908)
Solutions
- Use a double-quoted string literal instead of a single-quoted char constant for multi-character text.
- Ensure single-quoted literals contain exactly one valid rune with correct escape syntax.
- Fix or remove invalid Unicode escape sequences in the char constant.
Example fix
// before
{{ 'ab' }}
// after
{{ "ab" }} Defensive patterns
Strategy: validation
Validate before calling
// Validate char literals before relying on a generated template.
func validCharLit(s string) bool {
if len(s) < 3 || s[0] != '\'' || s[len(s)-1] != '\'' { return false }
_, _, tail, err := strconv.UnquoteChar(s[1:], '\'')
return err == nil && tail == "'"
} Try / catch
_, err := template.New("t").Parse(body)
if err != nil {
// err contains the file:line; surface to the author
return err
} Prevention
- Prefer double-quoted string literals for any text longer than one rune.
- Lint templates during CI to catch malformed constants early.
When it happens
Trigger: A template literal such as {{ 'ab' }} (multi-rune), {{ '\u000G' }} (invalid escape), or a stray character constant with trailing characters.
Common situations: Copy-pasting Go syntax into templates expecting char semantics; editor auto-completion inserting characters; attempting to express a single quote literal incorrectly.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- illegal number syntax: %q
- template: no files named in call to ParseFiles
- integer overflow: %q
- template: %s:%d: %s
- can't divide the value by 0
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/05863a6439841bb1.
Report an issue: GitHub.