## Problem Updating credentials for an existing IMAP/SMTP/CalDAV connected account in **Settings → Accounts → Connection settings** has no effect on the sync. The save persists the new `connectionParameters`, but `messageChannel.syncStatus` / `messageChannel.syncStage` / `connectedAccount.authFailedAt` are left untouched, and no fetch job is queued. This matters most when the channel is in `FAILED_INSUFFICIENT_PERMISSIONS` (e.g. after Apple invalidates iCloud app-specific passwords, or on any other auth failure): `MessagingRelaunchFailedMessageChannelsCronJob` only retries `FAILED_UNKNOWN`, so the account is stuck on "Sync failed" forever despite the credentials now being correct. The only known workarounds are a direct DB update or deleting and recreating the account. #19273 fixed the frontend cache angle of credential editing; this PR fixes the backend half of the same UX (the channel state machine). ## Reproduce 1. Connect an IMAP/SMTP account. 2. Force an auth failure (e.g. revoke the app-specific password upstream). Wait until `messageChannel.syncStatus` flips to `FAILED_INSUFFICIENT_PERMISSIONS`. 3. Generate a fresh password, edit the account in **Settings → Accounts**, save. 4. Observe: account stays "Sync failed" indefinitely; `core.messageChannel.syncStatus` and `core.connectedAccount.authFailedAt` are unchanged; no IMAP connect attempt in the worker logs. ## Root cause `packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.ts → processAccount` saves the updated `connectionParameters` but never resets the sync state nor enqueues a fetch job. The OAuth providers handle this: | Reset step | `google-apis.service.ts` | `microsoft-apis.service.ts` | `imap-smtp-caldav-apis.service.ts` (before this PR) | |---|---|---|---| | `updateConnectedAccountOnReconnect` (clears `authFailedAt`) | yes | yes | — | | `accountsToReconnectService.removeAccountToReconnect` | yes | yes | — | | `resetAndMarkAsMessagesListFetchPending` | yes | yes | — | | Enqueue `MessagingMessageListFetchJob` | yes | yes | — | | `resetAndMarkAsCalendarEventListFetchPending` | yes | yes | — | | Enqueue `CalendarEventListFetchJob` | yes | yes | — | #12061 introduced this behaviour for Google/Microsoft. The IMAP service was added later and the equivalent reconnect plumbing was never ported. ## Fix Mirrors the Google/Microsoft pattern in `processAccount`: - **Inside** the transaction, when an account already exists: clear `authFailedAt` on the connected account. - **After** the transaction, when an existing account is being updated: - drop the account from `accountsToReconnect` user-vars, - if the message channel exists and IMAP is configured, call `resetAndMarkAsMessagesListFetchPending` and enqueue `MessagingMessageListFetchJob` (skipped while the channel is still `PENDING_CONFIGURATION`), - same logic for the calendar channel and `CalendarEventListFetchJob`. Wires `MessageChannelSyncStatusService`, `CalendarChannelSyncStatusService`, `AccountsToReconnectService` and the messaging/calendar queues into `IMAPAPIsModule`. ## Tests - Extended the existing `should preserve existing channels when updating account credentials` case to assert: `authFailedAt: null` is written within the transaction; `removeAccountToReconnect` is called with the resolved `userId`; `resetAndMarkAs*` and queue `add` are called for both channels. - New case: `should not queue fetch jobs for channels still in PENDING_CONFIGURATION`. - New case: `should not run reconnect logic when creating a brand new account`. I could not run the full server test suite locally (no `node_modules` checked out); relying on CI. ## Out of scope - Extending `UpdateConnectedAccountOnReconnectService` to a non-OAuth shape: kept inline to minimise the blast radius. Refactoring opportunity for a follow-up. - Behaviour when the user removes IMAP or CALDAV from the parameters on update (the channel currently lingers in its old state). Pre-existing and not made worse by this PR.
The #1 Open-Source CRM
Website ·
Documentation ·
Roadmap ·
Discord ·
Figma
Why Twenty
Twenty gives technical teams the building blocks for a custom CRM that meets complex business needs and quickly adapts as the business evolves. Twenty is the CRM you build, ship, and version like the rest of your stack.
Learn more about why we built Twenty
Installation
Cloud
The fastest way to get started. Sign up at twenty.com and spin up a workspace in under a minute, with no infrastructure to manage and always up to date.
Build an app
Scaffold a new app with the Twenty CLI:
npx create-twenty-app my-app
Define objects, fields, and views as code:
import { defineObject, FieldType } from 'twenty-sdk/define';
export default defineObject({
nameSingular: 'deal',
namePlural: 'deals',
labelSingular: 'Deal',
labelPlural: 'Deals',
fields: [
{ name: 'name', label: 'Name', type: FieldType.TEXT },
{ name: 'amount', label: 'Amount', type: FieldType.CURRENCY },
{ name: 'closeDate', label: 'Close Date', type: FieldType.DATE_TIME },
],
});
Then ship it to your workspace:
npx twenty deploy
See the app development guide for objects, views, agents, and logic functions.
Self-hosting
Run Twenty on your own infrastructure with Docker Compose, or contribute locally via the local setup guide.
Everything you need
Twenty gives you the building blocks of a modern CRM (objects, views, workflows, and agents) and lets you extend them as code. Here's a tour of what's in the box.
Want to go deeper? Read the User Guide for product walkthroughs, or the
Documentation for developer reference.
|
|
|
|
|
|
Stack
TypeScript
Nx
NestJS, with BullMQ,
PostgreSQL,
Redis
React, with Jotai, Linaria and Lingui
Thanks
Thanks to these amazing services that we use and recommend for UI testing (Chromatic), code review (Greptile), catching bugs (Sentry) and translating (Crowdin).
Join the Community
Star the repo ·
Discord ·
Feature requests ·
Releases ·
X ·
LinkedIn ·
Crowdin ·
Contribute





