{"record":{"id":"7a3b64cc183b2bd5","repo":"github/github-mcp-server","slug":"failed-to-get-upload-url-w-7a3b64","errorCode":null,"errorMessage":"failed to get upload URL: %w","messagePattern":"failed to get upload URL: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/github/dependencies.go","lineNumber":322,"sourceCode":"\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\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)","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/dependencies.go#L304-L340","documentation":"RequestDeps.GetClient resolves the uploads URL (used for release-asset uploads) through the injected utils.APIHostResolver before constructing the REST client. With the bundled utils.APIHost the uploads URL is parsed once in NewAPIHost (dotcom, GHEC, or GHES with/without subdomain isolation), so UploadURL never errors; this wrap appears only with custom resolvers or miswired dependency structs. It aborts every REST-based tool call until fixed.","triggerScenarios":"deps.GetClient(ctx) with a custom APIHostResolver whose UploadURL(ctx) errors, or a resolver built without an uploads URL (returning nil URL and an error). GHES setups hit URL-derivation bugs only at NewAPIHost time (startup), not here. Standard utils.NewAPIHost never produces this at request time.","commonSituations":"Library integrators implementing APIHostResolver for an internal gateway and forgetting the uploads endpoint; proxies that only front api.* and not uploads.*; test fakes with stub UploadURL returning errors; upgrading utils.APIHostResolver interface signature and missing implementations.","solutions":["Use utils.NewAPIHost(GITHUB_HOST) so upload URL derivation (including GHES subdomain-isolation probing) and validation happen once at startup","If implementing APIHostResolver, make UploadURL return a pre-parsed *url.URL (e.g. https://uploads.github.com for dotcom) and never fail per request","Preflight all five resolver methods (BaseRESTURL, GraphqlURL, UploadURL, RawURL, AuthorizationServerURL) during process init and fail fast","For upload-dependent tools only, consider resolving lazily but caching the result so a transient resolver error surfaces once, not per call"],"exampleFix":"// before\ntype r struct{}\nfunc (r) UploadURL(ctx context.Context) (*url.URL, error) {\n\treturn nil, fmt.Errorf(\"uploads not configured\") // every GetClient fails\n}\n\n// after: derive and cache at construction\ntype r struct{ upload *url.URL }\nfunc newR(base string) (*r, error) {\n\tu, err := url.Parse(base) // e.g. https://uploads.github.com\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &r{upload: u}, nil\n}\nfunc (x r) UploadURL(context.Context) (*url.URL, error) { return x.upload, nil }","handlingStrategy":"validation","validationCode":"// preflight the uploads URL before serving traffic\nup, err := apiHosts.UploadURL(context.Background())\nif err != nil || up == nil || !up.IsAbs() {\n\tlog.Fatalf(\"uploads URL unusable: %v\", err)\n}","typeGuard":"func hasValidUploadURL(a utils.APIHostResolver) bool {\n\tu, err := a.UploadURL(context.Background())\n\treturn err == nil && u != nil && u.IsAbs()\n}","tryCatchPattern":null,"preventionTips":["Derive uploads.<host> in the same constructor that parses the base host","Preflight all five APIHostResolver methods at startup","Disable or gate upload-dependent tools if your gateway cannot reach the uploads host"],"tags":["go","configuration","dependency-injection","uploads","github-api"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}