GitHub
Create GitHub issues from Unhandled issues or link existing ones, and resolve the Unhandled issue when the GitHub issue closes.
The GitHub integration turns an Unhandled issue into a GitHub issue in the right repository, or links one that already exists. When the GitHub issue is closed, Unhandled resolves its issue for you. It connects through a GitHub App installation, so access is limited to the repositories you grant and nobody's personal token is involved.
Connect
You need to be an organization admin.
- Open the organization's Integrations settings (
/o/:org/settings/integrations) and click Connect next to GitHub. - GitHub opens the Unhandled app's installation page. Pick the account and the repositories it may access, then install.
- GitHub asks you to authorize the Unhandled app, unless you already did. Unhandled uses that authorization once, to check that GitHub lists the installation for your GitHub account and that you own the account it is installed on or are an admin of that organization.
- GitHub sends you back to the Integrations page, where GitHub shows Connected with the account name.
The connect link carries an encrypted state that expires after 10 minutes, works once, and only completes for the admin who started it in the same browser session. Collaborators who can only see some of the installation's repositories cannot connect it.
Map repositories
Click Configure next to GitHub and pick one repository per project. The list shows up to 100 repositories the installation can reach. A project's repository is where its new GitHub issues are created and where Link existing searches. To change the repositories the app can see, edit the installation on GitHub.
Create a GitHub issue
On an issue, open the menu and choose Create or link GitHub issue, then Create new. The title starts as the issue title and can be edited. The body is filled in for you:
**TypeError**: Cannot read properties of undefined (reading 'total')
- Application: Shop
- Environments: production
- First seen: 2026-10-19T08:12:44.000Z
- Last seen: 2026-10-21T14:03:10.000Z
[View in Unhandled](https://unhandled.example.com/o/acme/projects/shop/issues/412)Unhandled sets no labels, assignees or milestone. If the project has no mapped repository, creation fails with "Link a repository to this project first".
Link an existing issue
Choose Link existing instead. Search runs is:issue in the project's mapped repository and shows up to 20 results with their state. You can also type a reference as owner/repo#number. A reference to the mapped repository works for anyone who can see the project. A reference to any other repository the installation can access needs an organization admin; for everyone else, linking fails with "Only an admin can use a repository other than the one linked to this project". The same rule applies to creating and linking through the API and the MCP server. Linking is recorded in the issue's activity, and Unlink in the same menu removes it. The link_issue tool of the MCP server also accepts a full GitHub issue URL.
Closing resolves the issue
Unhandled listens to the app's webhooks:
- When a linked GitHub issue is closed, Unhandled resolves the issue, if it is open or archived for a limited time, and records "resolved externally" in its activity.
- When it is reopened, Unhandled updates the link's state but leaves the issue as it is.
- When the app is uninstalled on GitHub, Unhandled removes the GitHub connection.
Nothing flows the other way: resolving in Unhandled does not close the GitHub issue, and Unhandled never comments on it. If the exception happens again after the issue was resolved, it regresses as usual.
Disconnect
Disconnect on the Integrations page removes the connection. Project mappings and existing links are kept for when you connect again. Uninstall the app on GitHub too if you want to revoke its repository access.
Self-hosting
Create a GitHub App for your Unhandled server and set these environment variables:
| Variable | Value |
|---|---|
GITHUB_APP_SLUG | The app's URL slug, used for https://github.com/apps/<slug>/installations/new |
GITHUB_APP_ID | The app ID |
GITHUB_APP_PRIVATE_KEY | The app's PEM private key; \n sequences are turned into newlines |
GITHUB_APP_WEBHOOK_SECRET | The app's webhook secret |
GITHUB_APP_CLIENT_ID | The app's client ID (not the app ID) |
GITHUB_APP_CLIENT_SECRET | A client secret generated in the app settings |
In the app settings:
- Set the setup URL to
https://unhandled.example.com/integrations/github/callback, so GitHub returnsinstallation_idandstatethere after installation. - Add the same address as a callback URL. Unhandled sends the admin through GitHub's user authorization to that address. Leave Request user authorization (OAuth) during installation off, since Unhandled starts that step itself.
- Set the webhook URL to
https://unhandled.example.com/webhooks/github. Unhandled verifies thex-hub-signature-256header and acts onissuesevents with theclosedandreopenedactions and oninstallationevents with thedeletedaction. - Grant read and write access to issues, since Unhandled creates, reads and searches them.
- Grant read access to the organization's members, so Unhandled can check that the admin who connects an organization installation is an admin of that GitHub organization.