sipeed/picoclaw · error

failed to open env file: %w

Error message

failed to open env file: %w

What it means

loadEnvFile opens the env_file attached to an MCP server definition (KEY=value lines, # comments) with os.Open and wraps a failure here. The path is opened as given — relative paths resolve against the process working directory, not the config file location, which is the most common cause.

Source

Thrown at pkg/mcp/manager.go:71

		req.Header.Set(key, value)
	}

	// Use the base transport
	base := t.base
	if base == nil {
		base = http.DefaultTransport
	}
	return base.RoundTrip(req)
}

// loadEnvFile loads environment variables from a file in .env format
// Each line should be in the format: KEY=value
// Lines starting with # are comments
// Empty lines are ignored
func loadEnvFile(path string) (map[string]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("failed to open env file: %w", err)
	}
	defer file.Close()

	envVars := make(map[string]string)
	scanner := bufio.NewScanner(file)
	lineNum := 0

	for scanner.Scan() {
		lineNum++
		line := strings.TrimSpace(scanner.Text())

		// Skip empty lines and comments
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		// Parse KEY=value
		parts := strings.SplitN(line, "=", 2)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Use an absolute path for env_file in the MCP server config
  2. Create the file if missing (even empty) so Open succeeds
  3. chmod/chown so the running user can read it
  4. If the file is optional, remove the env_file key rather than pointing at a nonexistent path

Example fix

# before
mcp:
  servers:
    myserver:
      env_file: .env      # resolved against process cwd

# after
mcp:
  servers:
    myserver:
      env_file: /etc/picoclaw/myserver.env
Defensive patterns

Strategy: validation

Validate before calling

for name, srv := range cfg.MCP.Servers {
    if srv.EnvFile == "" {
        continue
    }
    if _, err := os.Stat(srv.EnvFile); err != nil {
        return fmt.Errorf("mcp server %s env_file %s unusable: %w", name, srv.EnvFile, err)
    }
}

Try / catch

if err := mcp.StartServers(cfg); err != nil {
    if strings.Contains(err.Error(), "failed to open env file") {
        // path is resolved against the process cwd — make env_file absolute and retry
        return errors.New("env_file not found: use an absolute path in the MCP server config")
    }
    return err
}

Prevention

When it happens

Trigger: (1) ENOENT: relative env_file like .env or ../secrets.env resolved from a different cwd (service started from another directory); file never created; typo in the path; (2) EACCES: file present but not readable by the process user.

Common situations: Starting the app from a different directory than during testing (relative .env worked interactively, fails as a daemon); env_file referenced in a shared MCP config that lacks the file on this machine; files created with restrictive umask or root ownership.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/cb2786ac7c0ee712. Report an issue: GitHub.