JuliusBrussee/caveman · critical
githubapp: invalid base URL
Error message
githubapp: invalid base URL
What it means
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.
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.
Example fix
// before app, _ := githubapp.New(keyPEM, appID, instID, "api.github.com") // after app, _ := githubapp.New(keyPEM, appID, instID, "https://api.github.com")
Defensive patterns
Strategy: validation
Validate before calling
func validBaseURL(s string) error {
u, err := url.Parse(s)
if err != nil { return err }
if u.Scheme == "" || u.Host == "" || u.User != nil {
return fmt.Errorf("base URL must be absolute with host and no userinfo: %q", s)
}
return nil
}
// call before constructing the App Try / catch
app, err := githubapp.New(keyPEM, appID, instID, cfg.BaseURL)
if err != nil {
// fail fast at startup: log the configured value and exit
log.Fatalf("invalid github base URL %q: %v", cfg.BaseURL, err)
} Prevention
- 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.
When it happens
Trigger: 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().
Common situations: 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".
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- githubapp: invalid request path
- githubapp: request path escaped configured host
- githubapp: request path must be a single-host absolute path
- provider upstream URL must be an absolute URL with a host
- provider upstream URL must not include a fragment
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/ae75ce1625e6cc63.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/githubapp/githubapp.go:354
}
return decoded, nil
}
// DoToken issues an authenticated GitHub REST call with an installation token and
// returns the status + raw body for the caller to parse. It is the reusable
// primitive the worker's PR opener builds the Git Data API flow on, so every
// GitHub egress goes through the one SSRF-guarded client + fixed base host.
func (a *App) DoToken(ctx context.Context, token, method, path string, body any) (int, []byte, error) {
return a.do(ctx, "Bearer "+token, method, path, body)
}
func (a *App) do(ctx context.Context, authorization, method, path string, body any) (int, []byte, error) {
if !strings.HasPrefix(path, "/") || strings.HasPrefix(path, "//") || strings.Contains(path, "\\") {
return 0, nil, fmt.Errorf("githubapp: request path must be a single-host absolute path")
}
base, err := url.Parse(a.baseURL)
if err != nil || base.Scheme == "" || base.Host == "" || base.User != nil {
return 0, nil, fmt.Errorf("githubapp: invalid base URL")
}
relative, err := url.ParseRequestURI(path)
if err != nil || relative.IsAbs() || relative.Host != "" || relative.User != nil {
return 0, nil, fmt.Errorf("githubapp: invalid request path")
}
target, err := url.Parse(a.baseURL + path)
if err != nil || target.Scheme != base.Scheme || !strings.EqualFold(target.Host, base.Host) || target.User != nil {
return 0, nil, fmt.Errorf("githubapp: request path escaped configured host")
}
var reader io.Reader
if body != nil {
b, err := json.Marshal(body)
if err != nil {
return 0, nil, fmt.Errorf("githubapp: marshal request: %w", err)
}
reader = bytes.NewReader(b)
}
req, err := http.NewRequestWithContext(ctx, method, target.String(), reader)View on GitHub (pinned to 3ee70a1026)