Skip to content

Commit b8863a2

Browse files
committed
fix(zoho-desk): make createSubscription config failures non-retryable
createSubscription threw plain Errors (no status) for missing orgId, event type, or credentials, and for a Zoho success with no webhook id - so the deploy outbox mapped them to 500 and retried permanent configuration failures. Attach a 4xx via statusError (400 for missing config/credentials; 422 for the no-id anomaly, where a retry risks duplicate webhooks) so they fail the deploy terminally like the mapped Zoho API 4xx responses. Tests assert the 400 status on the guard paths.
1 parent 630517a commit b8863a2

2 files changed

Lines changed: 36 additions & 22 deletions

File tree

apps/sim/lib/webhooks/providers/zoho-desk.test.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,30 +83,32 @@ describe('zohoDeskHandler', () => {
8383
})
8484

8585
describe('createSubscription', () => {
86-
it('throws a clear error when the organization ID is missing', async () => {
87-
await expect(
88-
zohoDeskHandler.createSubscription?.({
89-
webhook: { providerConfig: { eventType: 'Ticket_Add' } },
86+
async function captureCreateError(providerConfig: Record<string, unknown>): Promise<unknown> {
87+
try {
88+
await zohoDeskHandler.createSubscription?.({
89+
webhook: { providerConfig },
9090
workflow: {},
9191
userId: 'user-1',
9292
requestId: 'test',
93-
// biome-ignore lint/suspicious/noExplicitAny: request is unused on this guard path
93+
// biome-ignore lint/suspicious/noExplicitAny: request is unused on these guard paths
9494
request: {} as any,
9595
})
96-
).rejects.toThrow(/Organization ID/i)
96+
} catch (error) {
97+
return error
98+
}
99+
return undefined
100+
}
101+
102+
it('fails terminally (400) when the organization ID is missing', async () => {
103+
const error = await captureCreateError({ eventType: 'Ticket_Add' })
104+
expect((error as Error)?.message).toMatch(/Organization ID/i)
105+
expect(errorStatus(error)).toBe(400)
97106
})
98107

99-
it('throws a clear error when the event type is missing', async () => {
100-
await expect(
101-
zohoDeskHandler.createSubscription?.({
102-
webhook: { providerConfig: { orgId: '700123' } },
103-
workflow: {},
104-
userId: 'user-1',
105-
requestId: 'test',
106-
// biome-ignore lint/suspicious/noExplicitAny: request is unused on this guard path
107-
request: {} as any,
108-
})
109-
).rejects.toThrow(/event type/i)
108+
it('fails terminally (400) when the event type is missing', async () => {
109+
const error = await captureCreateError({ orgId: '700123' })
110+
expect((error as Error)?.message).toMatch(/event type/i)
111+
expect(errorStatus(error)).toBe(400)
110112
})
111113
})
112114

apps/sim/lib/webhooks/providers/zoho-desk.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,30 @@ export const zohoDeskHandler: WebhookProviderHandler = {
158158
const orgId = typeof config.orgId === 'string' ? config.orgId : undefined
159159
const eventType = typeof config.eventType === 'string' ? config.eventType : undefined
160160

161+
// Missing configuration is permanent - carry a 4xx so the deploy outbox fails
162+
// terminally (NonRetryableDeploymentError) instead of retrying a create that
163+
// can never succeed without the user fixing the trigger config.
161164
if (!orgId) {
162-
throw new Error('Zoho Desk Organization ID is required to create the webhook subscription.')
165+
throw statusError(
166+
'Zoho Desk Organization ID is required to create the webhook subscription.',
167+
400
168+
)
163169
}
164170
if (!eventType) {
165-
throw new Error('A Zoho Desk event type is required to create the webhook subscription.')
171+
throw statusError(
172+
'A Zoho Desk event type is required to create the webhook subscription.',
173+
400
174+
)
166175
}
167176

168177
const owner = credentialId ? await getCredentialOwner(credentialId, requestId) : null
169178
const accessToken = owner
170179
? await refreshAccessTokenIfNeeded(owner.accountId, owner.userId, requestId)
171180
: null
172181
if (!accessToken || !owner) {
173-
throw new Error(
174-
'Zoho Desk account connection required. Please connect your Zoho Desk account in the trigger configuration and try again.'
182+
throw statusError(
183+
'Zoho Desk account connection required. Please connect your Zoho Desk account in the trigger configuration and try again.',
184+
400
175185
)
176186
}
177187

@@ -230,7 +240,9 @@ export const zohoDeskHandler: WebhookProviderHandler = {
230240
// Never persist a subscription without its id: the id is the JWT `aud` claim
231241
// verifyAuth checks, so an empty one would force verification to fail open.
232242
if (!externalId) {
233-
throw new Error('Zoho Desk webhook creation succeeded but returned no webhook id')
243+
// Zoho reported success but gave no id: a webhook may have been created and
244+
// is unidentifiable, so retrying risks duplicates. Fail terminally (4xx).
245+
throw statusError('Zoho Desk webhook creation succeeded but returned no webhook id', 422)
234246
}
235247

236248
logger.info(`[${requestId}] Created Zoho Desk webhook`, { externalId })

0 commit comments

Comments
 (0)