{"record":{"id":"8baeda074f3c92da","repo":"siyuan-note/siyuan","slug":"environment-w","errorCode":null,"errorMessage":"environment: %w","messagePattern":"environment: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/mcp.go","lineNumber":461,"sourceCode":"\tdefault:\n\t\treturn nil, nil, nil, fmt.Errorf(\"unsupported server type: %s\", server.Type)\n\t}\n}\n\nfunc connectStdio(ctx context.Context, client *mcp.Client, server conf.MCPServer) (*mcp.ClientSession, *exec.Cmd, error) {\n\tif server.Command == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"command is required for stdio server\")\n\t}\n\n\tcmd := exec.Command(server.Command, server.Args...)\n\tcmdEnv, err := buildStdioEnvironment(server, os.LookupEnv, func(value string) string {\n\t\tif model.Conf == nil {\n\t\t\treturn value\n\t\t}\n\t\treturn conf.ResolveSecretsVars(model.Conf.Secrets, model.Conf.Variables, value)\n\t}, runtime.GOOS)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"environment: %w\", err)\n\t}\n\tcmd.Env = cmdEnv\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"stdin pipe: %w\", err)\n\t}\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"stdout pipe: %w\", err)\n\t}\n\tcmd.Stderr = io.Discard\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"start command: %w\", err)\n\t}\n\n\tconnectCtx, connectCancel := context.WithTimeout(ctx, serverTimeout(server))\n\tdefer connectCancel()","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/mcp.go#L443-L479","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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'.","Open the MCP server configuration and fix the offending InheritEnv entry or Env key/value that the wrapped message names.","Run ValidateMCPServerEnvironment(server) on the edited config before persisting it so the error is surfaced at config-save time, not at process spawn.","On Windows, remember environmentKey upper-cases names, so remove case-variant duplicates (e.g. PATH vs Path) from both InheritEnv and Env."],"exampleFix":"// before\n\"env\": {\n  \"API_KEY\": \"secret\",\n  \"\": \"oops\"\n}\n// after\n\"env\": {\n  \"API_KEY\": \"secret\"\n}","handlingStrategy":"validation","validationCode":"// Run before persisting an MCP server config to surface env-validation errors early.\nfunc validateServerEnv(server conf.MCPServer) error {\n    return client.ValidateMCPServerEnvironment(server)\n}\n// Example:\n// if err := validateServerEnv(srv); err != nil { return err }","typeGuard":null,"tryCatchPattern":"// errors.Is/As on the wrapped validation error to branch UI messaging.\nvar envErr error\nif errors.As(err, &envErr) {\n    // show err.Error() in config UI, do not retry connect\n}","preventionTips":["Call ValidateMCPServerEnvironment at config-save time, not only at connect time.","When building config programmatically, filter empty names and de-duplicate inheritEnv before writing."],"tags":["mcp","stdio","environment","config-validation"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}