twpayne/chezmoi · error

%d: 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.

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 = value

View on GitHub (pinned to f901167e46)

Solutions

  1. Change the directive value to a non-negative integer, e.g. format-indent-width=2
  2. Check the template/config source for an accidental leading minus sign or a negative default leaking in
  3. If the value comes from code, clamp or validate it before building the option string
  4. 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

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.