Skip to content

fix(slack): dispatch app_uninstalled to onUninstalled - #1073

Merged
bensabic merged 4 commits into
vercel:mainfrom
mmarufov:mmarufov/github-issue-1057-fix
Oct 27, 2026
Merged

bensabic merged 4 commits into
vercel:mainfrom
mmarufov:mmarufov/github-issue-1057-fix

Conversation

@mmarufov

@mmarufov mmarufov commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Slack uninstall events currently never reach onUninstalled. In multi-workspace apps, a missing installation can also cause the event to be dropped before routing.

  • Dispatch app_uninstalled through the existing lifecycle handler for HTTP and Socket Mode, without resolving a bot token.
  • Use the installation key as event.conversationId, so handlers can clean up workspace or org-wide installations. event.tenantId is the Enterprise Grid enterprise_id when the payload carries one.
  • Skip the installation lookup for tokens_revoked too. It still doesn't call onUninstalled, but multi-workspace apps no longer log missing-installation warnings when it arrives after the installation is deleted.
  • Document the event subscription, add app_uninstalled to the Slack manifests, check the adapter name in every onUninstalled example, and add a minor changeset.

tokens_revoked doesn't call onUninstalled because Slack can revoke tokens while leaving the app installed (auth.revoke). Mapping it would report removals that didn't happen and duplicate callbacks during a full uninstall. app_uninstalled_team isn't mapped either: it tells org-wide admin apps that some other app was removed (reference).

Existing onUninstalled handlers only see Slack events once the Slack app subscribes to app_uninstalled. Apps already subscribed start receiving it when they upgrade, which the changeset calls out.

Refs #1057.

Test plan

  • uninstall.test.ts covers single- and multi-workspace revocation ordering, token-only revocation, authorizations taking precedence over top-level fields, the top-level fallback, Enterprise Grid tenantId, handler work registered with waitUntil, HTTP and Socket Mode retries, and missing initialization and event IDs.
  • installation.test.ts checks that the Teams docs example ignores a Slack uninstall that would otherwise match a Teams record.
  • pnpm validate passes.

Payloads follow Slack's documented examples; no live uninstall was performed. The org-wide fixture is synthetic and unverified against live traffic.

Checklist

  • All commits are signed and verified
  • All commits are signed off for the DCO (git commit -s)
  • pnpm validate passes
  • Changeset added
  • Documentation updated

Signed-off-by: Muhammadjon Marufov <marufov254@gmail.com>
@mmarufov
mmarufov requested a review from a team October 20, 2026 05:37
@vercel

vercel Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@mmarufov is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

@roboeve roboeve Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, the implementation looks correct and the new suite is solid: the 15 tests pass locally along with the full @chat-adapter/slack suite, and coverage shows every new line in extractInstallationFromEvent, the app_uninstalled early return in resolveEventRequestContext, and the dispatch path is exercised.

Requesting changes to close a few test gaps, noted inline:

  1. tokens_revoked ordering is only tested in single-workspace mode (botToken set), so the multi-workspace path never runs for it. The interesting case is multi-workspace with the handler deleting the installation, then tokens_revoked arriving: it should hit the "unresolved" branch, return 200, and not call onUninstalled again.
  2. Two defensive branches in handleAppUninstalled are uncovered: the !this.chat guard and the event_id ?? "" fallback.
  3. The Socket Mode test calls routeSocketEvent directly with no retry_num, so redelivery dedup over Socket Mode isn't covered for this event.
  4. The org-wide fixture is synthetic (as the PR notes). If a real org-wide app_uninstalled envelope can be captured, adding it to sample-messages.md and the test would confirm authorizations[0] carries enterprise_id with is_enterprise_install: true and a null team_id. If not, a short comment on the fixture saying it is unverified would help future readers.

Comment thread packages/adapter-slack/src/uninstall.test.ts Outdated
Comment thread packages/adapter-slack/src/uninstall.test.ts Outdated
Comment thread packages/adapter-slack/src/uninstall.test.ts
Comment thread packages/adapter-slack/src/uninstall.test.ts
Comment thread packages/adapter-slack/src/index.ts
Comment thread packages/adapter-slack/src/index.ts
mmarufov and others added 3 commits October 27, 2026 14:00
Signed-off-by: Muhammadjon Marufov <marufov254@gmail.com>
Signed-off-by: Muhammadjon Marufov <marufov254@gmail.com>
…on uninstalls

tokens_revoked events now skip the installation lookup, like
app_uninstalled, so multi-workspace apps no longer log missing-installation
warnings when one arrives after the installation is deleted. Slack
uninstall events set tenantId to the Enterprise Grid enterprise_id when
the payload carries one.

Add app_uninstalled to the Slack manifests in the docs and README, check
the adapter name in the onUninstalled examples, note that
deleteInstallation doesn't remove records from an installationProvider
store, and update the Teams page now that Slack emits onUninstalled.

Tests cover authorizations taking precedence over top-level fields, the
top-level fallback, handler work registered with waitUntil, and
app_uninstalled_team not dispatching. A new integration test checks that
the Teams docs example ignores a Slack uninstall.

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>

@roboeve roboeve Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test gaps from my earlier review are addressed in 272ff46 and e1cfed8: multi-workspace tokens_revoked ordering, the uninitialized-adapter and missing event_id branches, Socket Mode retry dedup, and the org-wide fixture is now marked unverified. The uninstall suite passes locally.

@bensabic
bensabic merged commit 80fa77b into vercel:main Oct 27, 2026
22 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants