air-verse/air · error
failed to parse %s: %w
Error message
failed to parse %s: %w
What it means
Air wraps the underlying TOML decode failure when parsing a user-supplied config file. If the config file exists but cannot be parsed (invalid TOML syntax, wrong types), air returns this wrapped error instead of silently falling back to defaults. Defaults are only used when no config file exists.
Source
Thrown at runner/config.go:390
return "", fmt.Errorf("failed to write to %s: %w", dftTOML, err)
}
return dftTOML, nil
}
// defaultPathConfig loads `.air.toml` or `.config/air.toml` when present.
// The bool is true when a config file was loaded, false when defaults are returned.
func defaultPathConfig() (*Config, bool, error) {
// when path is blank, first find `.air.toml` in `air_wd` and current working directory or .config/air.toml, if not found, use defaults
for _, name := range []string{dftTOML, cfgTOML} {
cfg, err := readConfByName(name)
if err == nil {
return cfg, true, nil
}
// If the config file exists but failed to parse, report the error
// Only use defaults if no config file exists
if !os.IsNotExist(err) {
return nil, false, fmt.Errorf("failed to parse %s: %w", name, err)
}
}
dftCfg := defaultConfig()
return &dftCfg, false, nil
}
func readConfByName(name string) (*Config, error) {
var path string
if wd := os.Getenv(airWd); wd != "" {
path = filepath.Join(wd, name)
} else {
wd, err := os.Getwd()
if err != nil {
return nil, err
}
path = filepath.Join(wd, name)
}View on GitHub (pinned to 71ea1dee05)
Solutions
- Read the wrapped `%w` cause to find the exact TOML line/column and fix the syntax
- Validate the file with a TOML linter (e.g. `tomlcheck .air.toml` or taplo)
- If unsure, regenerate a known-good config with `air init` and re-apply your customizations
- Ensure the file extension matches its content (.toml for TOML)
Example fix
// before (.air.toml) [build] bin = "tmp/main // unterminated string // after [build] bin = "tmp/main"
Defensive patterns
Strategy: validation
Validate before calling
// before running air
func configValid(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil // air will use defaults
}
var cfg map[string]any
_, err := toml.DecodeFile(path, &cfg)
return err
} Try / catch
if err := runAir(); err != nil {
var perr *toml.ParseError
if errors.As(err, &perr) {
log.Fatalf("fix config syntax: %v", perr) // points at line/col
}
log.Fatal(err)
} Prevention
- Validate .air.toml with a TOML linter in CI or a pre-commit hook
- Regenerate config with `air init` when upgrading air versions
- Keep one canonical config and symlink instead of hand-copying
- Avoid mixing JSON/YAML into .toml files
When it happens
Trigger: Running `air` (or air's config-loading path via parseConfig) with a .air.toml/.air.conf file that exists but contains malformed TOML or a field with an incompatible type; parseFile decodes it with the toml library and the decode error is wrapped here.
Common situations: Hand-edited config with a missing quote, unbalanced bracket, or tab/indentation mistake; copying a config from an older air version with renamed/retyped fields; accidentally writing JSON or YAML into a .toml file.
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
- configuration already exists
- entrypoint values must be strings, got %T
- entrypoint must be a string or array of strings, got %T
- build.rules[%d] (%s): cmd is required
- build.rules[%d] (%s): at least one of include_dir, include_e
AI-assisted analysis of air-verse/air@71ea1dee05 (2026-08-31).
Data as JSON: /api/errors/4c3439947f603633.
Report an issue: GitHub.