JuliusBrussee/caveman · error

githubapp: app id is required

Error message

githubapp: app id is required

What it means

githubapp.New validates its Config and refuses to build the App when cfg.AppID is empty (after trimming whitespace). The constructor is deliberately fail-closed: no half-built App, so connect endpoints can answer a clean "disabled" instead of fabricating success.

Solutions

  1. Set the GitHub App ID in configuration (env var or config field) and pass it into Config.AppID
  2. Trim and validate the value at config-load time; treat empty as "GitHub integration disabled" and skip calling New
  3. Check deployment secrets/env were actually mounted (e.g. missing secret in the orchestrator)
  4. Verify the config key name matches what the loader reads (typos yield empty strings silently)

Example fix

// before
app, err := githubapp.New(githubapp.Config{PrivateKeyPEM: pem})
// after
if cfg.GitHubAppID == "" {
    return nil, nil // integration disabled
}
app, err := githubapp.New(githubapp.Config{AppID: cfg.GitHubAppID, PrivateKeyPEM: pem})
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(cfg.GitHubAppID) == "" {
    // treat as integration disabled or fail config load here
    return ErrGitHubAppUnconfigured
}
app, err := githubapp.New(githubapp.Config{AppID: cfg.GitHubAppID, PrivateKeyPEM: keyPEM})

Type guard

func appConfigured(app *githubapp.App) bool { return app != nil }

Try / catch

app, err := githubapp.New(cfg)
if err != nil {
    if strings.Contains(err.Error(), "app id is required") {
        log.Info("github app not configured; connect endpoints disabled")
        return nil // intentional disabled state
    }
    return err
}

Prevention

When it happens

Trigger: Calling githubapp.New with Config{AppID: ""} — e.g. the GitHub App ID env var or config file value was never set, or contains only spaces.

Common situations: Deploying without the GitHub App integration configured; an env-var lookup returning "" with no default; YAML/TOML key present but empty; config struct assembled before loading settings.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/98c7cbca47ea444a. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/githubapp/githubapp.go:69

}

// App holds the parsed App identity and the SSRF-guarded HTTP client.
type App struct {
	appID         string
	slug          string
	privateKey    *rsa.PrivateKey
	webhookSecret string
	baseURL       string
	httpClient    *http.Client
}

// New parses the private key, validates the base URL, and builds the App. It
// returns an error (not a half-built App) on any misconfiguration, so callers
// fail closed — an unconfigured deployment leaves the App nil and the connect
// endpoints answer a clean "disabled" rather than a fabricated success.
func New(cfg Config) (*App, error) {
	if strings.TrimSpace(cfg.AppID) == "" {
		return nil, fmt.Errorf("githubapp: app id is required")
	}
	if len(cfg.PrivateKeyPEM) == 0 {
		return nil, fmt.Errorf("githubapp: private key PEM is required")
	}
	key, err := parseRSAPrivateKey(cfg.PrivateKeyPEM)
	if err != nil {
		return nil, err
	}
	base := strings.TrimRight(strings.TrimSpace(cfg.BaseURL), "/")
	if base == "" {
		base = defaultBaseURL
	}
	client := cfg.HTTPClient
	if client == nil {
		// Production path: SSRF-guarded client + pre-flight host check on a custom
		// (GHE) base. When a caller injects a client (tests), it owns the host policy,
		// so we skip the pre-flight — but production never injects one.
		if base != defaultBaseURL {

View on GitHub (pinned to 3ee70a1026)