GitHub tool onboarding fails with "Tool authorization credentials are invalid" and 404 from GitHub API when using OAuth 2.0 with JWTIssue <!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } "Tool authorization credentials are invalid" error when connecting/onboarding a GitHub tool in the DevOps Change Velocity workspace using OAuth 2.0 with JSON Web Token (JWT) authentication. The error persists despite correct credentials being configured. Outbound HTTP logs show a 404 response from the GitHub API, and DevOps error logs report a `SCRIPT_INCLUDE_ERROR` from `DevOpsCredentialExpireHelper`. Symptoms<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } - The DevOps Change Workspace displays the following alert during the Connect a tool playbook: Alert level: Critical. Tool authorization credentials are invalid. Enter valid credentials, and try again. - The Tool Connection History on the GitHub tool record shows repeated **Disconnected** states with the message: Connection error: Proactive expiration cannot be handled due to a technical issue. - Outbound HTTP logs (`sys_outbound_http_log`) show a POST request to: `https://api.github.com/app/installations/<installation_id>/access_tokens` returning HTTP 404 with a response body of `{"message":"Not Found"}`. - DevOps error logs (`All > DevOps > Administration > Error Logs`) show: - devopsErrorCode: `SCRIPT_INCLUDE_ERROR` - `errorSources`: `DevOpsCredentialExpireHelper.handleProactiveExpirationForTool` - `internalMsg`: references `sys_script_include` file - System logs show two `DevOpsEncrypter` warnings at the time of each connection attempt: - `The provided text for encryption is empty.` - `The provided encryptedText for decryption is empty.` - The connection fails consistently across all application nodes in the cluster. - Each failed playbook connection attempt creates a new stale Connection and Credential Alias record with an epoch-zero timestamp in the name (e.g., `DevOps-_-19700101XXXXXX_<toolname>`). Facts<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } - Product: DevOps Change Velocity - Module: DevOps Change Workspace — GitHub tool onboarding - Credential type: OAuth 2.0 with JWT (GitHub Apps — JWT Bearer grant type) - GitHub type: GitHub.com (also applicable to GitHub Enterprise Server) - Authentication flow: JWT Bearer — the platform generates a signed JWT using a Java KeyStore (JKS) certificate and exchanges it for an installation access token via the GitHub API - OAuth API Script on the Application Registry record: `OAuthDevOpsGitHubJWTHandler` - Default Grant Type on the Application Registry record: JWT Bearer - Affected table: `jwt_provider` (JWT Provider record), `jwt_provider_claim` (Standard Claims) - Release: Zurich Patch 7 and potentially other releases [VERIFY for earlier/later releases] - The JWT Provider record contains three Standard Claims: `aud`, `iss`, and `sub` - The `iss` (issuer) claim is used by GitHub to identify the GitHub App making the request before processing the installation token exchange Release<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Zurich Patch 7. Behavior is expected to apply to any release where DevOps Change Velocity supports OAuth 2.0 with JWT for GitHub Apps. [VERIFY applicability on Vancouver, Washington DC, Xanadu] Cause<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } The `iss` (issuer) claim in the JWT Provider record is set to the GitHub App installation ID (a numeric value) instead of the GitHub App Client ID (a string value, formatted as `Iv23XXXXXXXXXXXXXXXX`). When the platform generates a JWT for the token exchange, it populates the `iss` field with the installation ID. GitHub receives this JWT and attempts to match the `iss` value against known GitHub App Client IDs. Because an installation ID is not a valid Client ID, GitHub cannot identify the App and returns an HTTP 404 before the installation access token can be issued. The platform then receives an empty token, which causes the `DevOpsEncrypter` warnings and the `SCRIPT_INCLUDE_ERROR` in `DevOpsCredentialExpireHelper`. The installation ID and the Client ID are two separate values: | Value | Format | Location in GitHub | | App ID | Numeric integer | GitHub App settings > About section | | Client ID | String (`Iv23...`) | GitHub App settings > About section | | Installation ID | Numeric integer | URL at `github.com/settings/installations/<id>` | Per GitHub documentation, the `iss` claim must contain either the Client ID (recommended) or the numeric App ID. The installation ID is never valid as an `iss` value. Resolution<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Step 1 — Enable DevOps debug logging (optional but recommended for diagnosis) Before making any changes, enable elevated logging to capture the full error context. 1. Navigate to All > DevOps > Administration > DevOps Properties. 2. Set the Log level property to Debug. 3. Reproduce the connection failure by attempting to connect the GitHub tool. 4. Navigate to All > DevOps > Administration > Error Logs and filter by the affected tool. 5. Open the most recent error entry and expand the Context map section to view the full `internalMsg`, `errorSources`, and `properties` fields. The errorSources field confirms whether DevOpsCredentialExpireHelper.handleProactiveExpirationForTool is in the call stack, which is the signature error for this root cause. Disable debug logging after diagnosis to avoid performance impact. Step 2 — Confirm the root cause via outbound HTTP logs 1. Navigate to All > System Logs > Outbound HTTP Requests. 2. Filter Created to the time of the most recent failed connection attempt. 3. Search for requests to `api.github.com`. 4. Open the log entry and check the Response status field. A 404 response to `https://api.github.com/app/installations/<installation_id>/access_tokens` with a response body of {"message":"Not Found"} confirms the JWT is being rejected by GitHub before the token exchange completes. This is the signature of an incorrect `iss` claim value. A 401 response instead indicates an expired or revoked credential. A 403 response indicates a valid credential with insufficient permissions. Step 3 — Identify the correct Client ID for the GitHub App 1. In GitHub.com, navigate to Settings > Developer Settings > GitHub Apps. 2. Open the relevant GitHub App. 3. In the About section, locate the Client ID field. The Client ID is a string value in the format `Iv23XXXXXXXXXXXXXXXX`. Do not use the numeric App ID listed in the same section, and do not use the installation ID. 4. Note this value — it is needed in Step 4. Step 4 — Correct the `iss` claim on the JWT Provider record 1. Navigate to All > System OAuth > JWT Providers. 2. Open the JWT Provider record associated with the GitHub App (for example, named `<org> - GitHub App JWT Provider`). 3. In the **Standard Claims** related list, open the `iss` row. 4. In the Claim Value field, replace the current numeric installation ID value with the GitHub App Client ID identified in Step 3 (for example, `Iv23XXXXXXXXXXXXXXXX`). 5. Click Update. Step 5 — Verify the Token URL contains the correct installation ID The Token URL on the Application Registry (OAuth Provider) record must contain the installation ID — this is separate from the `iss` claim corrected in Step 4. 1. Navigate to All > System OAuth > Application Registry. 2. Open the OAuth Provider record for the GitHub App. 3. Confirm the Token URL field is set to: `https://api.github.com/app/installations/<installation_id>/access_tokens` where `<installation_id>` is the numeric installation ID (not the Client ID and not the App ID). 4. To verify the installation ID, navigate to GitHub.com > Settings > Developer Settings > GitHub Apps > [app name] > Install App, click the gear icon next to the organization, and check the numeric ID in the browser URL: `https://github.com/settings/installations/<installation_id>` Step 6 — Clean up stale tool records and connection aliases Each failed playbook attempt creates orphaned records. Remove them before retrying. 1. Navigate to All > DevOps Tools (`sn_devops_tool_list.do`). 2. Delete any GitHub tool records created during failed onboarding attempts. 3. Navigate to All > Connection & Credential Aliases. 4. Delete any aliases related to the GitHub tool that contain epoch-zero timestamps in their name (entries with `197001` in the name). These are auto-generated during failed playbook runs and are not reusable. Step 7 — Retry the playbook connection 1. Navigate to Workspaces > DevOps Change Workspace. 2. Select Applications from the left navigation. 3. Open or create the relevant application. 4. Select Connect a tool and choose GitHub. 5. Work through the playbook steps using the existing JWT credentials. Note : Connect via the Applications module — not the homepage or Tools module — if repository, pipeline, and plan discovery is required. The homepage and Tools module entry points do not support tool object discovery. After a successful connection, the tool record Connection state field shows Connected and the Last discovery field is populated. Step 8 — Verify the fix via outbound HTTP logs 1. Navigate to All > System Logs > Outbound HTTP Requests. 2. Filter by the time of the retry attempt. 3. Confirm the POST request to `https://api.github.com/app/installations/<installation_id>/access_tokens` now returns HTTP 201. A 201 response confirms the JWT was accepted, the installation access token was issued, and the connection completed successfully. Related Links<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } - GitHub documentation — Generating a JSON Web Token (JWT) for a GitHub App - Setting up GitHub OAuth 2.0 credentials for DevOps Change Velocity - Permissions required for DevOps tools - Onboard GitHub to DevOps Change Velocity - KB1588984 — Encountering the error "Tool authorization credentials are invalid" when connecting the GitHub tool