{"record":{"id":"8baeda074f3c92da","repo":"siyuan-note/siyuan","slug":"environment-w","errorCode":null,"errorMessage":"environment: %w","messagePattern":"environment: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/mcp.go","lineNumber":463,"sourceCode":"\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\t// stdio 环境变量插值不受密钥 AllowedHosts 约束：目标是本地子进程而非网络主机，管理员在 Env 中\n\t// 引用 {{secrets.NAME}} 本身就是对该服务器的显式授权，与直接写入明文属于同一信任级别。\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":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/mcp.go#L445-L481","documentation":"Before spawning a stdio MCP server, buildStdioEnvironment assembles the subprocess environment from InheritEnv and Env entries, validating names and resolving {{secrets.*}}/{{vars.*}} interpolation. If validation or assembly fails (empty/invalid/duplicate names, bad key characters), connectStdio wraps the failure with this \"environment:\" prefix. The process is never started.","triggerScenarios":"connectStdio calls buildStdioEnvironment, which calls validateMCPServerEnvironment; an InheritEnv or Env entry has an empty name, a name containing '=' or NUL, a duplicate inherited name (case-insensitive on Windows), or an invalid value for the target OS.","commonSituations":"Config with an Env key written as \"FOO=bar\" instead of key \"FOO\", an empty key from malformed JSON, the same variable listed twice in InheritEnv, or secrets referencing undefined secret names.","solutions":["Fix the offending Env/InheritEnv entry: each name must be non-empty and contain no '=' or NUL","Move the value out of the name — write {\"Env\": {\"FOO\": \"bar\"}} not {\"FOO=bar\": \"...\"}","Remove duplicate entries from InheritEnv (Windows compares case-insensitively)","Verify any {{secrets.NAME}}/{{vars.NAME}} references resolve to defined secrets/variables"],"exampleFix":"// before\n\"env\": {\"API_KEY=x\": \"abc\"}\n// after\n\"env\": {\"API_KEY\": \"abc\"}","handlingStrategy":"validation","validationCode":"for name := range server.Env {\n    if name == \"\" || strings.ContainsAny(name, \"=\\x00\") {\n        return fmt.Errorf(\"invalid env name %q for server %s\", name, server.Name)\n    }\n}\nfor _, name := range server.InheritEnv {\n    if strings.ContainsAny(name, \"=\\x00\") {\n        return fmt.Errorf(\"invalid inheritEnv name %q\", name)\n    }\n}","typeGuard":"null","tryCatchPattern":"if _, err := buildStdioEnvironment(server, os.LookupEnv, resolve, runtime.GOOS); err != nil {\n    return fmt.Errorf(\"check Env/InheritEnv for server %q: %w\", server.Name, err)\n}","preventionTips":["Treat Env as a name-to-value map: names never contain '='","Avoid duplicate InheritEnv entries; on Windows names compare case-insensitively","Verify secret/variable interpolations resolve before saving the server config"],"tags":["mcp","environment","stdio","config-validation"],"backgroundTag":"invalid-env-var-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}