{"record":{"id":"32ae9aaef0702a6d","repo":"github/github-mcp-server","slug":"failed-to-create-rest-client-w-32ae9a","errorCode":null,"errorMessage":"failed to create REST client: %w","messagePattern":"failed to create REST client: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/github/dependencies.go","lineNumber":332,"sourceCode":"\ttoken := tokenInfo.Token\n\n\tbaseRestURL, err := d.apiHosts.BaseRESTURL(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get base REST URL: %w\", err)\n\t}\n\tuploadURL, err := d.apiHosts.UploadURL(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get upload URL: %w\", err)\n\t}\n\n\t// Construct REST client\n\trestClient, err := gogithub.NewClient(\n\t\tgogithub.WithAuthToken(token),\n\t\tgogithub.WithUserAgent(fmt.Sprintf(\"github-mcp-server/%s\", d.version)),\n\t\tgogithub.WithEnterpriseURLs(baseRestURL.String(), uploadURL.String()),\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create REST client: %w\", err)\n\t}\n\treturn restClient, nil\n}\n\n// GetGQLClient implements ToolDependencies.\nfunc (d *RequestDeps) GetGQLClient(ctx context.Context) (*githubv4.Client, error) {\n\t// extract the token from the context\n\ttokenInfo, ok := ghcontext.GetTokenInfo(ctx)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"no token info in context\")\n\t}\n\ttoken := tokenInfo.Token\n\n\t// Construct GraphQL client\n\t// We use NewEnterpriseClient unconditionally since we already parsed the API host\n\t// Wrap transport with GraphQLFeaturesTransport to inject feature flags from context,\n\t// matching the transport chain used by the remote server.\n\tgqlHTTPClient := &http.Client{","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/dependencies.go#L314-L350","documentation":"GetClient builds the REST client via gogithub.NewClient(WithAuthToken, WithUserAgent, WithEnterpriseURLs(baseRestURL.String(), uploadURL.String())). WithEnterpriseURLs re-parses both URL strings with url.Parse and checks they are absolute; any string the resolver produced that go-github cannot parse becomes this wrapped error. Because the strings come from already-parsed *url.URL values, it is nearly unreachable in the standard wiring and signals a malformed or nil-derived URL in a custom resolver.","triggerScenarios":"A custom APIHostResolver whose URL objects stringify to relative or control-character-laden URLs (e.g. \"/api/v3/\" with no scheme, or a URL built from unescaped user input); baseRestURL or uploadURL being nil, panicking before this line, in careless implementations; version drift where WithEnterpriseURLs adds new validation. go-github returns url.Parse errors or 'invalid URL' style errors here.","commonSituations":"Hand-rolled resolvers that construct URLs via string concatenation with unencoded paths; GHES hosts containing spaces or unicode in the hostname; mixed go-github versions after upgrading v89 where URL validation tightened; resolvers that return *url.URL values reconstructed from tenant-supplied headers.","solutions":["Inspect the wrapped error: it is the verbatim go-github/url error and names which URL string failed to parse","Ensure the resolver returns absolute https URLs (scheme + host), e.g. https://api.github.com/ and https://uploads.github.com","Reproduce outside the server: url.Parse(baseRestURL.String()) and url.Parse(uploadURL.String()) in a scratch test to find the malformed component","Percent-encode any tenant or user-supplied path segments before building the URL instead of raw concatenation","Pin/align the go-github version (v89 in this module) so WithEnterpriseURLs validation matches what you tested"],"exampleFix":"// before: concatenating an unvalidated host into a URL\nrestURL := &url.URL{Scheme: \"https\", Opaque: host + \"/api/v3/\"} // Opaque stringifies badly\nclient, err := gogithub.NewClient(gogithub.WithEnterpriseURLs(restURL.String(), uploadURL.String()))\n\n// after: build via structured fields so String() is always parseable\nu := &url.URL{Scheme: \"https\", Host: host, Path: \"/api/v3/\"}\nif err := validateAbsolute(u); err != nil { return nil, err }\nclient, err := gogithub.NewClient(gogithub.WithEnterpriseURLs(u.String(), upload.String()))","handlingStrategy":"validation","validationCode":"// verify both strings are parseable absolute URLs before client construction\nfor _, s := range []string{baseRestURL.String(), uploadURL.String()} {\n\tu, err := url.Parse(s)\n\tif err != nil || !u.IsAbs() {\n\t\treturn fmt.Errorf(\"unusable URL for go-github: %q\", s)\n\t}\n}","typeGuard":"func isAbsoluteURL(s string) bool {\n\tu, err := url.Parse(s)\n\treturn err == nil && u.IsAbs() && u.Host != \"\"\n}","tryCatchPattern":"// in GetClient-style code\nrestClient, err := gogithub.NewClient(opts...)\nif err != nil {\n\treturn nil, fmt.Errorf(\"failed to create REST client: %w\", err) // inspect wrapped url.Error via errors.Unwrap\n}","preventionTips":["Build *url.URL via structured fields (Scheme/Host/Path), never string concatenation","Percent-encode tenant-supplied path segments","Keep go-github version pinned to the one you tested (v89 here)"],"tags":["go","url-parsing","go-github","client-construction"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}