go-task/task · error

error reading env file %s: %w

Error message

error reading env file %s: %w

What it means

Task reads .env files listed under `dotenv:` with godotenv.Read. If the file exists but cannot be read or parsed (bad permissions, malformed KEY=VALUE lines), the OS/parser error is wrapped in this message and returned.

Source

Thrown at taskfile/dotenv.go:31

func Dotenv(vars *ast.Vars, tf *ast.Taskfile, dir string) (*ast.Vars, error) {
	env := ast.NewVars()
	cache := &templater.Cache{Vars: vars}

	for _, dotEnvPath := range tf.Dotenv {
		dotEnvPath = templater.Replace(dotEnvPath, cache)
		if dotEnvPath == "" {
			continue
		}
		dotEnvPath = filepathext.SmartJoin(dir, dotEnvPath)

		if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
			continue
		}

		envs, err := godotenv.Read(dotEnvPath)
		if err != nil {
			return nil, fmt.Errorf("error reading env file %s: %w", dotEnvPath, err)
		}
		for key, value := range envs {
			if _, ok := env.Get(key); !ok {
				env.Set(key, ast.Var{Value: value})
			}
		}
	}

	return env, nil
}

View on GitHub (pinned to 385e5ad92a)

Solutions

  1. Check file permissions so the Task process user can read the file (`chmod 644 .env`)
  2. Fix dotenv syntax: each line must be KEY=VALUE (quote values containing spaces/special chars)
  3. Ensure the dotenv path points to a regular file, not a directory
  4. Look at the wrapped `%w` cause in the message for the exact OS/parser error

Example fix

# before (.env)
MY VAR = hello

# after
MY_VAR="hello"
Defensive patterns

Strategy: try-catch

Validate before calling

for _, f := range dotenvPaths {
    if fi, err := os.Stat(f); err == nil && fi.IsDir() {
        return fmt.Errorf("dotenv path %s is a directory", f)
    }
    if err := checkDotenvSyntax(f); err != nil {
        return err
    }
}

Type guard

func isReadableFile(path string) bool {
    fi, err := os.Stat(path)
    return err == nil && fi.Mode().IsRegular() && fi.Mode().Perm()&0o400 != 0
}

Try / catch

env, err := dotenv.Dotenv(vars, node, workingDir)
if err != nil {
    var osErr *fs.PathError
    if errors.As(err, &osErr) {
        // handle unreadable/missing dotenv file
    }
    return fmt.Errorf("loading dotenv: %w", err)
}

Prevention

When it happens

Trigger: readDotEnvFiles -> Dotenv: a dotenv path passes the os.Stat existence check but godotenv.Read fails — e.g. permission denied, path is a directory, or the file has invalid dotenv syntax.

Common situations: A .env file with unparseable lines (spaces around =, missing values on quoted lines, junk characters); read-only or root-owned .env in CI; a `dotenv:` entry pointing at a directory instead of a file.

Related errors


AI-assisted analysis of go-task/task@385e5ad92a (2026-09-05). Data as JSON: /api/errors/f8bff22b3a9ac061. Report an issue: GitHub.