{"record":{"id":"3eeaec349892967b","repo":"larksuite/cli","slug":"proxy-plugin-config-is-invalid-w","errorCode":null,"errorMessage":"proxy plugin config is invalid: %w","messagePattern":"proxy plugin config is invalid: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/transport/transport.go","lineNumber":23,"sourceCode":"\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sync\"\n)\n\n// proxyPluginTransport is a fixed-proxy clone of http.DefaultTransport (with optional\n// custom root CA), lazily built on first use when proxy plugin mode is enabled.\nvar proxyPluginTransport = sync.OnceValue(buildProxyPluginTransport)\n\n// cachedBlockedTransport is a fail-closed transport cached on first use when\n// the proxy plugin config exists but is invalid. This avoids cloning\n// http.DefaultTransport on every pluginTransport call.\nvar cachedBlockedTransport = sync.OnceValue(buildBlockedTransport)\n\nfunc buildBlockedTransport() http.RoundTripper {\n\treturn failClosedTransport(fmt.Errorf(\"proxy plugin config is invalid: %w\", loadErr))\n}\n\nfunc buildProxyPluginTransport() http.RoundTripper {\n\tdef, ok := http.DefaultTransport.(*http.Transport)\n\tif !ok {\n\t\t// Cannot clone the stdlib transport. Fail closed with a concrete\n\t\t// *http.Transport (not a bare RoundTripper) so downcasting callers such\n\t\t// as Fallback cannot silently degrade this into a\n\t\t// direct-egress transport.\n\t\treturn failClosedTransport(fmt.Errorf(\"proxy plugin transport unavailable: http.DefaultTransport is %T, want *http.Transport\", http.DefaultTransport))\n\t}\n\n\tcfg, err := Load()\n\tif err != nil {\n\t\t// Fail closed: config file exists but is malformed/unreadable — do not\n\t\t// silently fall back to direct egress.\n\t\treturn blockedTransport(def, fmt.Errorf(\"proxy plugin config is invalid: %w\", err))\n\t}","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/transport/transport.go#L5-L41","documentation":"The cached fail-closed transport wraps the stored loadErr: the proxy plugin config file exists but Load() returned an error (malformed or unreadable). Every request through pluginTransport is blocked with this error instead of silently using direct egress, and the result is cached to avoid rebuilding per call.","triggerScenarios":"Proxy plugin config file present but invalid JSON/permission-denied, then any CLI HTTP request goes through pluginTransport -> cachedBlockedTransport -> buildBlockedTransport.","commonSituations":"Hand-edited proxy config file with a JSON syntax error; config written by root with no read permission; partially written file from an interrupted update.","solutions":["Open the proxy plugin config file and fix JSON/schema errors (run a JSON validator).","Fix file permissions so the CLI user can read the config.","Delete the proxy plugin config file if proxy mode is not needed, so the CLI falls back to direct egress."],"exampleFix":"// before (proxy config)\n{\"enabled\": true, \"url\": \"http://proxy:8080\",,}\n// after\n{\"enabled\": true, \"url\": \"http://proxy:8080\"}","handlingStrategy":"try-catch","validationCode":"var cfg map[string]any\nif err := json.Unmarshal(cfgBytes, &cfg); err != nil {\n\tlog.Fatalf(\"proxy plugin config is not valid JSON: %v\", err)\n}","typeGuard":null,"tryCatchPattern":"tr, err := pluginTransport()\nif err != nil && strings.Contains(err.Error(), \"proxy plugin config is invalid\") {\n\tlog.Fatalf(\"fix proxy plugin config: %v\", err)\n}","preventionTips":["Generate the config with tooling (jq/yq) instead of hand-editing JSON.","Validate the config file after every write.","Remove the config file when proxy mode is not in use."],"tags":["proxy","configuration","fail-closed"],"backgroundTag":"invalid-proxy-configuration","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}