{"record":{"id":"e0c9cdd7ad5aac48","repo":"JuliusBrussee/caveman","slug":"githubapp-sign-app-jwt-w","errorCode":null,"errorMessage":"githubapp: sign app jwt: %w","messagePattern":"githubapp: sign app jwt: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/githubapp/githubapp.go","lineNumber":133,"sourceCode":"\theader := map[string]string{\"alg\": \"RS256\", \"typ\": \"JWT\"}\n\tclaims := map[string]any{\n\t\t\"iat\": now.Add(-60 * time.Second).Unix(),\n\t\t\"exp\": now.Add(10 * time.Minute).Unix(),\n\t\t\"iss\": a.appID,\n\t}\n\thb, err := json.Marshal(header)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcb, err := json.Marshal(claims)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsigningInput := base64.RawURLEncoding.EncodeToString(hb) + \".\" + base64.RawURLEncoding.EncodeToString(cb)\n\tdigest := sha256.Sum256([]byte(signingInput))\n\tsig, err := rsa.SignPKCS1v15(rand.Reader, a.privateKey, crypto.SHA256, digest[:])\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"githubapp: sign app jwt: %w\", err)\n\t}\n\treturn signingInput + \".\" + base64.RawURLEncoding.EncodeToString(sig), nil\n}\n\n// InstallationToken is the JIT, least-agency credential. It is scoped to the\n// single repo named and to the supplied permissions only, and is NEVER\n// persisted. The caller revokes it via RevokeToken at job end.\ntype InstallationToken struct {\n\tToken     string    `json:\"token\"`\n\tExpiresAt time.Time `json:\"expires_at\"`\n}\n\n// Installation is the App-authenticated identity GitHub assigns to an install.\n// Callers use it to reject callback-supplied ids that do not belong to this App.\ntype Installation struct {\n\tID      int64 `json:\"id\"`\n\tAccount struct {\n\t\tLogin string `json:\"login\"`","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/shared/platform/githubapp/githubapp.go#L115-L151","documentation":"Fires in AppJWT() when rsa.SignPKCS1v15 fails while signing the RS256 App JWT — the stored private key is inconsistent with the digest/parameters (e.g. key became invalid, or a mismatched key object). Rare in practice since New() already parses and validates the RSA key.","triggerScenarios":"Thrown at shared/platform/githubapp/githubapp.go:133 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Re-deploy with a valid GitHub App private key; regenerate the App key if it is corrupt","If persistent, verify the key loaded at New() is the same RSA key used here and that no in-memory corruption occurred"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}