{"record":{"id":"ae75ce1625e6cc63","repo":"JuliusBrussee/caveman","slug":"githubapp-invalid-base-url","errorCode":null,"errorMessage":"githubapp: invalid base URL","messagePattern":"githubapp: invalid base URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/githubapp/githubapp.go","lineNumber":354,"sourceCode":"\t}\n\treturn decoded, nil\n}\n\n// DoToken issues an authenticated GitHub REST call with an installation token and\n// returns the status + raw body for the caller to parse. It is the reusable\n// primitive the worker's PR opener builds the Git Data API flow on, so every\n// GitHub egress goes through the one SSRF-guarded client + fixed base host.\nfunc (a *App) DoToken(ctx context.Context, token, method, path string, body any) (int, []byte, error) {\n\treturn a.do(ctx, \"Bearer \"+token, method, path, body)\n}\n\nfunc (a *App) do(ctx context.Context, authorization, method, path string, body any) (int, []byte, error) {\n\tif !strings.HasPrefix(path, \"/\") || strings.HasPrefix(path, \"//\") || strings.Contains(path, \"\\\\\") {\n\t\treturn 0, nil, fmt.Errorf(\"githubapp: request path must be a single-host absolute path\")\n\t}\n\tbase, err := url.Parse(a.baseURL)\n\tif err != nil || base.Scheme == \"\" || base.Host == \"\" || base.User != nil {\n\t\treturn 0, nil, fmt.Errorf(\"githubapp: invalid base URL\")\n\t}\n\trelative, err := url.ParseRequestURI(path)\n\tif err != nil || relative.IsAbs() || relative.Host != \"\" || relative.User != nil {\n\t\treturn 0, nil, fmt.Errorf(\"githubapp: invalid request path\")\n\t}\n\ttarget, err := url.Parse(a.baseURL + path)\n\tif err != nil || target.Scheme != base.Scheme || !strings.EqualFold(target.Host, base.Host) || target.User != nil {\n\t\treturn 0, nil, fmt.Errorf(\"githubapp: request path escaped configured host\")\n\t}\n\tvar reader io.Reader\n\tif body != nil {\n\t\tb, err := json.Marshal(body)\n\t\tif err != nil {\n\t\t\treturn 0, nil, fmt.Errorf(\"githubapp: marshal request: %w\", err)\n\t\t}\n\t\treader = bytes.NewReader(b)\n\t}\n\treq, err := http.NewRequestWithContext(ctx, method, target.String(), reader)","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/githubapp/githubapp.go#L336-L372","documentation":"do() parses a.baseURL and requires a valid absolute URL with a scheme, a host, and no embedded userinfo. This error means the App was constructed with a baseURL that is unparseable, scheme-less, host-less, or carries credentials — the client cannot safely target it.","triggerScenarios":"Constructing the App (New) with baseURL set to \"\", \"localhost:8080\" (no scheme), \"/api/v3\" (no host), or \"https://user:pass@github.example.com\" (userinfo present), then calling any API method that reaches do().","commonSituations":"Empty or unset env var for a GitHub Enterprise base URL; writing \"api.github.com\" without \"https://\"; pasting a URL that includes credentials; typo like \"http:/api.github.com\".","solutions":["Set the base URL to a fully qualified absolute URL, e.g. \"https://api.github.com\" for GitHub.com or \"https://<ghe-host>/api/v3\" for Enterprise.","Strip any userinfo (user:pass@) from the configured base URL.","Validate the value with url.Parse in your config loader before constructing the App.","Check the env/config wiring so an empty variable doesn't silently become the baseURL."],"exampleFix":"// before\napp, _ := githubapp.New(keyPEM, appID, instID, \"api.github.com\")\n// after\napp, _ := githubapp.New(keyPEM, appID, instID, \"https://api.github.com\")","handlingStrategy":"validation","validationCode":"func validBaseURL(s string) error {\n    u, err := url.Parse(s)\n    if err != nil { return err }\n    if u.Scheme == \"\" || u.Host == \"\" || u.User != nil {\n        return fmt.Errorf(\"base URL must be absolute with host and no userinfo: %q\", s)\n    }\n    return nil\n}\n// call before constructing the App","typeGuard":null,"tryCatchPattern":"app, err := githubapp.New(keyPEM, appID, instID, cfg.BaseURL)\nif err != nil {\n    // fail fast at startup: log the configured value and exit\n    log.Fatalf(\"invalid github base URL %q: %v\", cfg.BaseURL, err)\n}","preventionTips":["Validate baseURL at config load / process start, not at first request.","Use https://api.github.com or https://HOST/api/v3 forms only.","Strip credentials from URLs before storing in config."],"tags":["github","configuration","url-validation"],"backgroundTag":"invalid-config-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}