{"record":{"id":"69f00e7e1c8ce690","repo":"github/github-mcp-server","slug":"failed-to-get-base-rest-url-w-69f00e","errorCode":null,"errorMessage":"failed to get base REST URL: %w","messagePattern":"failed to get base REST URL: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/github/dependencies.go","lineNumber":318,"sourceCode":"\t\tT:                 t,\n\t\tContentWindowSize: contentWindowSize,\n\t\tfeatureChecker:    featureChecker,\n\t\tobsv:              obsv,\n\t}\n}\n\n// GetClient implements ToolDependencies.\nfunc (d *RequestDeps) GetClient(ctx context.Context) (*gogithub.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\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","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/dependencies.go#L300-L336","documentation":"RequestDeps.GetClient resolves the REST base URL through its utils.APIHostResolver before building a go-github client. The bundled utils.APIHost parses everything eagerly in NewAPIHost, so its BaseRESTURL never fails; this wrap fires only when a custom APIHostResolver (remote-server variant, proxy, test fake) returns an error or a resolver was wired in without a REST URL. It is a dependency-wiring/configuration error, not an API error.","triggerScenarios":"Calling deps.GetClient(ctx) when d.apiHosts is a non-standard APIHostResolver whose BaseRESTURL(ctx) errors, e.g. a resolver that derives URLs per-request from tenant headers or one constructed without a parsed REST URL. Never triggers with utils.NewAPIHost output, which validates GITHUB_HOST (scheme present, https except loopback, GHEC https) at startup.","commonSituations":"Embedding github-mcp-server as a library and passing a homemade APIHostResolver; remote multi-tenant deployments resolving hosts per request; test doubles that return errors; upgrading the resolver interface and forgetting REST URL handling.","solutions":["If you use the standard host config, set GITHUB_HOST to a full URL with scheme (e.g. https://ghe.example.com) and rely on NewAPIHost, which fails fast at startup instead of per request","If you implement APIHostResolver, ensure BaseRESTURL returns a parsed non-nil *url.URL and reserve errors for genuinely unresolvable hosts","Preflight the resolver once at startup (call BaseRESTURL on a background context) and fail process init rather than every tool call","Check the wrapped error chain with errors.Unwrap to see the resolver's own message before debugging go-github"],"exampleFix":"// before: resolver that can fail per request\ntype tenantResolver struct{ host string }\nfunc (t tenantResolver) BaseRESTURL(ctx context.Context) (*url.URL, error) {\n\treturn url.Parse(t.host) // may fail on every GetClient call\n}\n\n// after: resolve once, validate at startup\ntype tenantResolver struct{ rest *url.URL }\nfunc newTenantResolver(host string) (*tenantResolver, error) {\n\tu, err := url.Parse(host)\n\tif err != nil || u.Scheme == \"\" {\n\t\treturn nil, fmt.Errorf(\"invalid host %q: must be a URL with scheme\", host)\n\t}\n\treturn &tenantResolver{rest: u}, nil\n}\nfunc (t tenantResolver) BaseRESTURL(context.Context) (*url.URL, error) { return t.rest, nil }","handlingStrategy":"validation","validationCode":"// startup preflight: fail init, not every tool call\nrest, err := apiHosts.BaseRESTURL(context.Background())\nif err != nil || rest == nil || !rest.IsAbs() {\n\tlog.Fatalf(\"invalid API host resolver: REST URL unusable: %v\", err)\n}","typeGuard":"func hasValidRESTResolver(a utils.APIHostResolver) bool {\n\tu, err := a.BaseRESTURL(context.Background())\n\treturn err == nil && u != nil && u.IsAbs() && u.Host != \"\"\n}","tryCatchPattern":null,"preventionTips":["Construct the resolver once via utils.NewAPIHost and reuse it","Validate GITHUB_HOST (scheme + https) at process start","Never implement APIHostResolver methods that can fail per request; resolve and cache at construction"],"tags":["go","configuration","dependency-injection","github-api","url-parsing"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}