siyuan-note/siyuan · error

environment: %w

Error message

environment: %w

What it means

Wraps any error returned by buildStdioEnvironment when preparing the inherited/explicit environment for an MCP stdio child process. The underlying cause is almost always one of the validation failures in validateMCPServerEnvironment (empty name, invalid characters, duplicate variable, or a NUL byte in a value). The '%w' verb preserves the wrapped error so callers can inspect it.

Source

Thrown at kernel/mcp/client/mcp.go:461

	default:
		return nil, nil, nil, fmt.Errorf("unsupported server type: %s", server.Type)
	}
}

func connectStdio(ctx context.Context, client *mcp.Client, server conf.MCPServer) (*mcp.ClientSession, *exec.Cmd, error) {
	if server.Command == "" {
		return nil, nil, fmt.Errorf("command is required for stdio server")
	}

	cmd := exec.Command(server.Command, server.Args...)
	cmdEnv, err := buildStdioEnvironment(server, os.LookupEnv, func(value string) string {
		if model.Conf == nil {
			return value
		}
		return conf.ResolveSecretsVars(model.Conf.Secrets, model.Conf.Variables, value)
	}, runtime.GOOS)
	if err != nil {
		return nil, nil, fmt.Errorf("environment: %w", err)
	}
	cmd.Env = cmdEnv
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, nil, fmt.Errorf("stdin pipe: %w", err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, nil, fmt.Errorf("stdout pipe: %w", err)
	}
	cmd.Stderr = io.Discard

	if err := cmd.Start(); err != nil {
		return nil, nil, fmt.Errorf("start command: %w", err)
	}

	connectCtx, connectCancel := context.WithTimeout(ctx, serverTimeout(server))
	defer connectCancel()

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Inspect the wrapped error (errors.Unwrap or %w chain) to see which validate* rule fired: 'name is empty', 'invalid name %q', 'duplicate inherited variable %q', 'variable %q contains NUL', or 'duplicate variable %q'.
  2. Open the MCP server configuration and fix the offending InheritEnv entry or Env key/value that the wrapped message names.
  3. Run ValidateMCPServerEnvironment(server) on the edited config before persisting it so the error is surfaced at config-save time, not at process spawn.
  4. On Windows, remember environmentKey upper-cases names, so remove case-variant duplicates (e.g. PATH vs Path) from both InheritEnv and Env.

Example fix

// before
"env": {
  "API_KEY": "secret",
  "": "oops"
}
// after
"env": {
  "API_KEY": "secret"
}
Defensive patterns

Strategy: validation

Validate before calling

// Run before persisting an MCP server config to surface env-validation errors early.
func validateServerEnv(server conf.MCPServer) error {
    return client.ValidateMCPServerEnvironment(server)
}
// Example:
// if err := validateServerEnv(srv); err != nil { return err }

Try / catch

// errors.Is/As on the wrapped validation error to branch UI messaging.
var envErr error
if errors.As(err, &envErr) {
    // show err.Error() in config UI, do not retry connect
}

Prevention

When it happens

Trigger: Call connectStdio (via the connectMCP flow, server.Type == "stdio") with a server whose InheritEnv or Env map fails validateMCPServerEnvironment: empty name, name containing '=' or NUL, duplicate inherited/explicit variable (case-insensitive on Windows), or a value containing NUL.

Common situations: User edits the MCP server config JSON by hand and adds an env entry like "=value" or "", lists the same PATH twice under inheritEnv, or pastes a value containing a stray control character. On Windows, listing both "PATH" and "Path" in inheritEnv triggers the duplicate check.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/8baeda074f3c92da. Report an issue: GitHub.