different-ai/openwork · error · PluginArchRouteFailure
github_install_org_mismatch
github_install_org_mismatch
Error message
GitHub install state does not match the current organization.
What it means
Thrown (409) in completeGithubConnectorInstall when the signed install `state` parameter's embedded orgId does not match the organization in the current actor context. The state is a one-time payload minted at install start; completing the callback in a different organization than the one that initiated it is rejected.
Source
Thrown at ee/apps/den-api/src/routes/org/plugin-system/store.ts:6896
wrapGithubConnectorError(error)
}
const state = createGithubInstallStateToken({
orgId: input.context.organizationContext.organization.id,
returnPath,
secret: env.betterAuthSecret,
userId: input.context.organizationContext.currentMember.userId,
})
return {
redirectUrl: buildGithubAppInstallUrl({ app, state }),
state,
}
}
export async function completeGithubConnectorInstall(input: { context: PluginArchActorContext; installationId: number; state: string }) {
const parsedState = consumeGithubInstallState(input.state)
if (parsedState.orgId !== input.context.organizationContext.organization.id) {
throw new PluginArchRouteFailure(409, "github_install_org_mismatch", "GitHub install state does not match the current organization.")
}
if (parsedState.userId !== input.context.organizationContext.currentMember.userId) {
throw new PluginArchRouteFailure(409, "github_install_user_mismatch", "GitHub install state does not match the current user.")
}
const connectorAccount = await upsertGithubConnectorAccountFromInstallation({
context: input.context,
installationId: input.installationId,
})
return {
connectorAccount,
// Keep install completion fast. The connected-account screen loads repositories next.
repositories: [],
}
}
export async function getGithubConnectorDiscovery(input: { connectorInstanceId: ConnectorInstanceId; context: PluginArchActorContext }) {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Restart the install from the organization where you want the connector: call startGithubConnectorInstall in that org's context and use the fresh state.
- Ensure the client keeps the active organization stable across the install round trip (don't switch orgs mid-flow).
- If building automation, pass the same organization context to both start and complete calls.
Example fix
// before
// started install in org A, callback handled under org B
await completeGithubConnectorInstall({ installationId, state }) // 409 github_install_org_mismatch
// after
switchOrganizationContext(orgA) // restore the org that initiated the install
await completeGithubConnectorInstall({ installationId, state }) Defensive patterns
Strategy: try-catch
Try / catch
try {
await completeGithubConnectorInstall({ installationId, state })
} catch (e) {
if (e instanceof PluginArchRouteFailure && e.code === 'github_install_org_mismatch') {
await restartInstallInCurrentOrg() // mint fresh state bound to the active org
} else throw e
} Prevention
- Never switch active organization between install start and callback
- Treat install state as single-use, org-bound, and short-lived
- Deep-link users back into the exact org context that started the install
When it happens
Trigger: Starting a GitHub install in org A, then completing the callback while signed into org B; replaying an install-state token from another org; user switching active org between install start and the GitHub redirect back.
Common situations: Users belonging to multiple organizations click the GitHub callback link from a bookmark or another browser tab where their active org changed; reusing an old install URL after switching workspaces.
Related errors
- github_install_user_mismatch
- plugin_ref_not_found
- plugin_manifest_not_found
- plugin_ambiguous
- invalid_github_materialization_context
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/94259d43e13d4281.
Report an issue: GitHub.