Skip to content

Commit 85284e6

Browse files
committed
fix(ci): make the utils gate see the wrapped forms of what it bans
`check-utils-enforcement.ts` scanned line by line, and every idiom it bans is a multi-token expression the formatter wraps at 100 columns. So it printed `✓ No banned patterns found` while eleven files carried the wrapped form of e instanceof Error ? e.message : fallback which CLAUDE.md mandates `getErrorMessage` for. The same class as the two blind spots already fixed in check-react-query-patterns. Patterns now run against the whole file, with match offsets mapped back to line numbers by binary search over the line-start table — verified against every offset of a multi-line fixture. Eight of the eleven are now `getErrorMessage(error, fallback)`. `auto-layout-utils` collapses a redundant `instanceof ApiClientError` arm on the way, since that class extends `Error`; `upgrade.ts` keeps its `rawBody ?? message` arm, which the helper cannot express, and only its tail collapses. The other three stay, because the helper genuinely does not fit, and they carry a `// utils-lint-allow: <reason>` annotation — the same escape hatch check-react-query-patterns already has, which this gate lacked: - the two auth routes return the message to an unauthenticated caller, so a non-Error throw must surface the fixed copy rather than its own text. `getErrorMessage` passes a thrown string straight through, which is the disclosure shape #7015 closed. - `e2b.ts` probes E2B's own error shape — a record-like carrying `message` or `value` — which has no equivalent. An annotation with no reason does not suppress, so the hatch cannot be used to silence a finding without saying why. Also corrects the header, which claimed Biome's `noRestrictedImports` covers "crypto named imports". It lists only `nanoid` and `uuid`. Named crypto imports pass both gates deliberately — server code building cipher IVs wants node's crypto, not the cross-context wrapper — and the comment asserting otherwise would mislead the next person auditing this. Verified the gate can fail in both directions: reintroducing a wrapped ternary reports it, and emptying an annotation's reason reports it too.
1 parent 445ef62 commit 85284e6

12 files changed

Lines changed: 93 additions & 48 deletions

File tree

apps/sim/app/api/auth/forget-password/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
9797
return NextResponse.json(
9898
{
9999
message:
100+
// utils-lint-allow: returned to an unauthenticated caller, so a non-Error throw
101+
// must surface the fixed copy rather than its own text — getErrorMessage would
102+
// pass a thrown string straight through.
100103
error instanceof Error
101104
? error.message
102105
: 'Failed to send password reset email. Please try again later.',

apps/sim/app/api/auth/reset-password/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
6060
return NextResponse.json(
6161
{
6262
message:
63+
// utils-lint-allow: returned to an unauthenticated caller, so a non-Error throw
64+
// must surface the fixed copy rather than its own text — getErrorMessage would
65+
// pass a thrown string straight through.
6366
error instanceof Error
6467
? error.message
6568
: 'Failed to reset password. Please try again or request a new reset link.',

apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,7 @@ export function TeamManagement({
274274
portalWindow?.close()
275275
logger.error('Failed to open billing portal from transfer dialog', { error })
276276
setTransferPortalError(
277-
error instanceof Error
278-
? error.message
279-
: 'Failed to open Stripe billing portal. Please try again.'
277+
getErrorMessage(error, 'Failed to open Stripe billing portal. Please try again.')
280278
)
281279
},
282280
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { createLogger } from '@sim/logger'
22
import { getErrorMessage } from '@sim/utils/errors'
33
import type { Edge } from 'reactflow'
4-
import { ApiClientError } from '@/lib/api/client/errors'
54
import { requestJson } from '@/lib/api/client/request'
65
import {
76
putWorkflowNormalizedStateContract,
@@ -100,12 +99,7 @@ export async function applyAutoLayoutAndUpdateStore(
10099
},
101100
})
102101
} catch (error) {
103-
const errorMessage =
104-
error instanceof ApiClientError
105-
? error.message
106-
: error instanceof Error
107-
? error.message
108-
: 'Auto layout failed'
102+
const errorMessage = getErrorMessage(error, 'Auto layout failed')
109103
logger.error('Auto layout API call failed:', { error: errorMessage })
110104
return { success: false, error: errorMessage }
111105
}

apps/sim/lib/billing/client/upgrade.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,7 @@ export function useSubscriptionUpgrade() {
211211
error:
212212
transferError instanceof ApiClientError
213213
? (transferError.rawBody ?? transferError.message)
214-
: transferError instanceof Error
215-
? transferError.message
216-
: 'Unknown error',
214+
: getErrorMessage(transferError, 'Unknown error'),
217215
})
218216
}
219217
} catch (error) {

apps/sim/lib/execution/remote-sandbox/e2b.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ function isE2BExecutionTimeout(error: unknown): boolean {
109109
? error.name
110110
: ''
111111
const message =
112+
// utils-lint-allow: probes E2B's own error shape — a record-like carrying `message`
113+
// or `value` — which getErrorMessage cannot express.
112114
error instanceof Error
113115
? error.message
114116
: isRecordLike(error)

apps/sim/lib/webhooks/providers/microsoft-teams.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { account } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { safeCompare } from '@sim/security/compare'
55
import { hmacSha256Base64 } from '@sim/security/hmac'
6-
import { toError } from '@sim/utils/errors'
6+
import { getErrorMessage, toError } from '@sim/utils/errors'
77
import { isRecordLike } from '@sim/utils/object'
88
import { eq } from 'drizzle-orm'
99
import { type NextRequest, NextResponse } from 'next/server'
@@ -733,9 +733,7 @@ export const microsoftTeamsHandler: WebhookProviderHandler = {
733733
error
734734
)
735735
throw new Error(
736-
error instanceof Error
737-
? error.message
738-
: 'Failed to create Teams subscription. Please try again.'
736+
getErrorMessage(error, 'Failed to create Teams subscription. Please try again.')
739737
)
740738
}
741739
},

apps/sim/lib/webhooks/providers/telegram.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { db, webhook, workflowDeploymentVersion } from '@sim/db'
22
import { createLogger } from '@sim/logger'
3+
import { getErrorMessage } from '@sim/utils/errors'
34
import { and, eq, isNull, ne } from 'drizzle-orm'
45
import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/provider-subscription-utils'
56
import type {
@@ -170,9 +171,7 @@ export const telegramHandler: WebhookProviderHandler = {
170171
error
171172
)
172173
throw new Error(
173-
error instanceof Error
174-
? error.message
175-
: 'Failed to create Telegram webhook. Please try again.'
174+
getErrorMessage(error, 'Failed to create Telegram webhook. Please try again.')
176175
)
177176
}
178177
},

apps/sim/lib/webhooks/providers/typeform.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createLogger } from '@sim/logger'
22
import { safeCompare } from '@sim/security/compare'
33
import { hmacSha256Base64 } from '@sim/security/hmac'
4+
import { getErrorMessage } from '@sim/utils/errors'
45
import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/provider-subscription-utils'
56
import type {
67
DeleteSubscriptionContext,
@@ -168,9 +169,7 @@ export const typeformHandler: WebhookProviderHandler = {
168169
error
169170
)
170171
throw new Error(
171-
error instanceof Error
172-
? error.message
173-
: 'Failed to create Typeform webhook. Please try again.'
172+
getErrorMessage(error, 'Failed to create Typeform webhook. Please try again.')
174173
)
175174
}
176175
},

apps/sim/lib/workspace-files/orchestration/file-folder-lifecycle.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
22
import { createLogger } from '@sim/logger'
3-
import { getPostgresErrorCode, toError } from '@sim/utils/errors'
3+
import { getErrorMessage, getPostgresErrorCode, toError } from '@sim/utils/errors'
44
import { asOrchestrationError, type OrchestrationErrorCode } from '@/lib/core/orchestration/types'
55
import { FolderPathError } from '@/lib/folders/paths'
66
import { notifyWorkspaceFilesChanged } from '@/lib/realtime/notify'
@@ -385,10 +385,10 @@ export async function performMoveWorkspaceFileItems(
385385
) {
386386
return {
387387
success: false,
388-
error:
389-
error instanceof Error
390-
? error.message
391-
: 'A file or folder with this name already exists in the destination folder',
388+
error: getErrorMessage(
389+
error,
390+
'A file or folder with this name already exists in the destination folder'
391+
),
392392
errorCode: 'conflict',
393393
}
394394
}

0 commit comments

Comments
 (0)