twpayne/chezmoi · error
: invalid format-indent-width
Error message
%d: invalid format-indent-width
What it means
chezmoi's template engine accepts a `format-indent-width` template directive that controls the indentation string used when re-formatting template output (e.g. JSON). The directive's value is parsed with strconv.Atoi and must be a non-negative integer; if the parsed number is negative, parseAndRemoveDirectives returns this error and template parsing fails. It is thrown to reject nonsensical indentation values at parse time rather than producing misformatted output.
Solutions
- Change the directive value to a non-negative integer, e.g. format-indent-width=2
- Check the template/config source for an accidental leading minus sign or a negative default leaking in
- If the value comes from code, clamp or validate it before building the option string
- If indentation should be disabled, use format-indent=false or a different directive instead of a negative width
Example fix
// before
options = append(options, "format-indent-width=-1")
// after
if width >= 0 {
options = append(options, fmt.Sprintf("format-indent-width=%d", width))
} Defensive patterns
Strategy: validation
Validate before calling
func validIndentWidth(v string) bool {
n, err := strconv.Atoi(v)
return err == nil && n >= 0
}
// only add "format-indent-width=N" when validIndentWidth(N-string) Try / catch
tpl, err := chezmoi.ParseTemplate(name, data, options)
if err != nil {
var nf *strconv.NumError
if strings.Contains(err.Error(), "invalid format-indent-width") || errors.As(err, &nf) {
// fall back to default options without the directive
}
return err
} Prevention
- Validate integer option values (>= 0) before adding them to template options
- Use named constants/enums for indentation instead of raw ints that may hold negative sentinels
- Add a unit test that parses every generated directive combination
- Prefer format-indent-width=0 over negative values when indentation should collapse
When it happens
Trigger: Calling chezmoi.ParseTemplate (or executing a template via parseAndRemoveDirectives) with a directive `format-indent-width` whose value parses to a negative integer, e.g. `{{ template "x" format-indent-width=-1 }}`-style option parsing where value is "-1", "-4", etc.
Common situations: Users hand-editing .chezmoitemplates or config template options and typo a minus sign; programmatically generating template options from config where a default of -1 means 'unset' and is passed through; copy-pasting examples that use negative sentinel values.
Related errors
AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01).
Data as JSON: /api/errors/b2ebbdf4fb3ae33e.
Report an issue: GitHub.
Appendix: source
Thrown at internal/chezmoi/template.go:176
case "utf-16-be":
o.Encoding = unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
case "utf-16-be-bom":
o.Encoding = unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
case "utf-16-le":
o.Encoding = unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
case "utf-16-le-bom":
o.Encoding = unicode.UTF16(unicode.LittleEndian, unicode.UseBOM)
default:
return nil, fmt.Errorf("%s: unknown encoding", value)
}
case "format-indent":
o.FormatIndent = value
case "format-indent-width":
switch width, err := strconv.Atoi(value); {
case err != nil:
return nil, err
case width < 0:
return nil, fmt.Errorf("%d: invalid format-indent-width", width)
default:
o.FormatIndent = strings.Repeat(" ", width)
}
case "left-delimiter":
o.LeftDelimiter = value
case "line-ending", "line-endings":
switch string(keyValuePairMatch[2]) {
case "crlf":
o.LineEnding = "\r\n"
case "lf":
o.LineEnding = "\n"
case "native":
o.LineEnding = nativeLineEnding
default:
o.LineEnding = value
}
case "right-delimiter":
o.RightDelimiter = valueView on GitHub (pinned to f901167e46)