{"record":{"id":"6017693e10c50e24","repo":"larksuite/cli","slug":"proxy-plugin-transport-unavailable-http-defaulttr","errorCode":null,"errorMessage":"proxy plugin transport unavailable: http.DefaultTransport is %T, want *http.Transport","messagePattern":"proxy plugin transport unavailable: http\\.DefaultTransport is %T, want \\*http\\.Transport","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/transport.go","lineNumber":33,"sourceCode":"var 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}\n\tif cfg == nil || !cfg.Enabled() {\n\t\treturn def\n\t}\n\tt, err := cfg.ApplyToTransport(def)\n\tif err != nil {\n\t\t// Fail closed: do not silently fall back to direct egress when the\n\t\t// operator explicitly enabled proxy plugin mode.\n\t\treturn blockedTransport(def, fmt.Errorf(\"proxy plugin enabled but config is invalid: %w\", err))\n\t}\n\treturn t","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/transport/transport.go#L15-L51","documentation":"The proxy plugin transport builder expects http.DefaultTransport to be a *http.Transport so it can clone it; a test or embedding host replaced it with a custom RoundTripper implementation. The builder fails closed with a blocked transport rather than allowing direct egress, since it cannot safely apply proxy settings to an unknown RoundTripper.","triggerScenarios":"Code (usually tests like TestBuildProxyPluginTransport_NonTransportDefaultFailsClosed, or a host app) assigns a non-*http.Transport to http.DefaultTransport, then the proxy plugin path builds its transport.","commonSituations":"Libraries that globally override http.DefaultTransport (metrics, mocking, custom dialers) running in-process with the CLI; tests swapping DefaultTransport without restoring it.","solutions":["Remove or defer the http.DefaultTransport override so the stdlib *http.Transport is in place when the CLI initializes.","If the override is required, install a *http.Transport (e.g. a clone of the default) instead of a bare RoundTripper.","Restore http.DefaultTransport with t.Cleanup/defer in tests before exercising CLI transport setup."],"exampleFix":"// before\nhttp.DefaultTransport = myMetricsRoundTripper{}\n// after\nif def, ok := http.DefaultTransport.(*http.Transport); ok {\n\tcloned := def.Clone()\n\tcloned.Proxy = nil\n\thttp.DefaultTransport = &instrumentedTransport{base: cloned}\n}","handlingStrategy":"type-guard","validationCode":"if _, ok := http.DefaultTransport.(*http.Transport); !ok {\n\tlog.Fatal(\"http.DefaultTransport must remain a *http.Transport for the CLI proxy plugin\")\n}","typeGuard":"func defaultTransportIsClonable() bool {\n\t_, ok := http.DefaultTransport.(*http.Transport)\n\treturn ok\n}","tryCatchPattern":"if tr, err := pluginTransport(); err != nil && strings.Contains(err.Error(), \"want *http.Transport\") {\n\tlog.Fatalf(\"remove the http.DefaultTransport override in this process: %v\", err)\n}","preventionTips":["Avoid globally replacing http.DefaultTransport in hosts embedding the CLI.","In tests, restore http.DefaultTransport with defer/t.Cleanup.","Wrap *http.Transport clones rather than bare RoundTripper implementations."],"tags":["proxy","http-transport","fail-closed"],"backgroundTag":"unsupported-default-transport","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"}