From 8d7bbbc67042aac35883d95235e39cc6eeafdeb9 Mon Sep 17 00:00:00 2001 From: Waleed Date: Fri, 15 May 2026 17:31:27 -0700 Subject: [PATCH] chore(utils): migrate to shared random/ID utilities and add enforcement linting (#4623) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(utils): migrate to shared random/ID utilities and add enforcement linting - Replace all Math.random(), crypto.randomUUID(), crypto.randomBytes(), nanoid, and uuid usages with shared @sim/utils/random and @sim/utils/id helpers across 72 files - Add new @sim/utils exports: deepClone, omit, filterUndefined (object), truncate (string), backoffWithJitter, parseRetryAfter (retry), getErrorMessage (errors) - Sweep all getErrorMessage, sleep, deepClone callsites across 500+ files to use shared utilities - Add Biome noRestrictedImports rule to catch nanoid, uuid, and crypto named imports at lint time - Add scripts/check-utils-enforcement.ts to catch Math.random and crypto.* global property access - Add check:utils script to package.json * chore(utils): replace deepClone wrapper with structuredClone built-in deepClone() was a one-line wrapper around structuredClone(), which is universally available in Node 17+ and all modern browsers. Removing the abstraction reduces indirection and means contributors don't need to learn a project-specific name for a well-known built-in. - Remove deepClone from packages/utils/src/object.ts and index.ts - Replace all 17 call sites with structuredClone() directly - Update check:utils script suggestion text - Update CLAUDE.md and global.md docs * fix(utils): add missing biome noRestrictedImports rule and correct truncate docs - Add noRestrictedImports to biome.json under style — bans nanoid and uuid package imports at lint time (crypto.randomUUID/randomBytes are caught by the check:utils grep script which handles global property access) - Correct truncate() TSDoc and parameter name: sliceLength makes it clear that total output length is sliceLength + suffix.length, matching the behavior all callers were already written to expect * fix(utils): add missing getErrorMessage imports at 4 call sites The sweep agents added getErrorMessage calls without the corresponding import in 4 files, causing test failures. Added the missing imports. * fix(utils): fix build errors from getErrorMessage sweep and retry.ts Turbopack issue - Fix retry.ts cross-file import: Turbopack cannot resolve './random.js' for internal package imports; inline the jitter crypto call directly - Add missing getErrorMessage imports to 32 files where the sweep added calls without the corresponding import (caught by type-check and test runs) - Remove accidental getErrorMessage import from crowdstrike/query/route.ts which has its own domain-specific getErrorMessage for parsing CrowdStrike's JSON error format - Fix use-sub-block-value.ts type error from structuredClone narrowing: add 'as T' cast at emitValue callsite (safe — valueCopy is always a structural copy of newValue) * fix(tools): use toError in crowdstrike catch block instead of local getErrorMessage The catch block was calling the local getErrorMessage function which parses CrowdStrike API JSON responses, not JavaScript Error objects. Use toError(error).message to correctly extract the message from a caught value in this context. --- .claude/rules/global.md | 25 ++- CLAUDE.md | 9 +- apps/realtime/src/database/operations.ts | 3 +- apps/realtime/src/handlers/operations.ts | 7 +- apps/realtime/src/handlers/subblocks.ts | 5 +- apps/realtime/src/handlers/variables.ts | 5 +- apps/realtime/src/index.test.ts | 5 +- apps/sim/app/(auth)/login/login-form.tsx | 6 +- .../reset-password/reset-password-content.tsx | 3 +- apps/sim/app/api/a2a/serve/[agentId]/route.ts | 3 +- apps/sim/app/api/admin/mothership/route.ts | 5 +- apps/sim/app/api/audit-logs/route.ts | 3 +- apps/sim/app/api/auth/oauth/token/route.ts | 3 +- apps/sim/app/api/auth/sso/register/route.ts | 7 +- apps/sim/app/api/billing/switch-plan/route.ts | 4 +- apps/sim/app/api/chat/manage/[id]/route.ts | 5 +- apps/sim/app/api/chat/route.ts | 5 +- apps/sim/app/api/copilot/chat/abort/route.ts | 7 +- apps/sim/app/api/copilot/chat/stream/route.ts | 9 +- apps/sim/app/api/copilot/confirm/route.ts | 12 +- apps/sim/app/api/copilot/credentials/route.ts | 3 +- apps/sim/app/api/copilot/feedback/route.ts | 3 +- .../api/copilot/training/examples/route.ts | 3 +- apps/sim/app/api/copilot/training/route.ts | 3 +- .../api/credential-sets/memberships/route.ts | 3 +- apps/sim/app/api/files/multipart/route.ts | 3 +- apps/sim/app/api/files/parse/route.ts | 6 +- apps/sim/app/api/files/presigned/route.ts | 5 +- apps/sim/app/api/files/upload/route.ts | 4 +- .../sim/app/api/folders/[id]/restore/route.ts | 3 +- apps/sim/app/api/form/manage/[id]/route.ts | 5 +- apps/sim/app/api/form/route.ts | 5 +- apps/sim/app/api/form/validate/route.ts | 3 +- .../documents/[documentId]/chunks/route.ts | 3 +- .../app/api/knowledge/[id]/documents/route.ts | 3 +- .../knowledge/[id]/documents/upsert/route.ts | 3 +- .../app/api/knowledge/[id]/restore/route.ts | 3 +- apps/sim/app/api/knowledge/search/route.ts | 7 +- apps/sim/app/api/mothership/execute/route.ts | 6 +- apps/sim/app/api/notifications/poll/route.ts | 3 +- apps/sim/app/api/organizations/route.ts | 9 +- .../api/providers/fireworks/models/route.ts | 3 +- .../app/api/providers/ollama/models/route.ts | 3 +- .../api/providers/openrouter/models/route.ts | 3 +- apps/sim/app/api/providers/route.ts | 4 +- .../app/api/providers/vllm/models/route.ts | 3 +- apps/sim/app/api/skills/import/route.ts | 3 +- apps/sim/app/api/speech/token/route.ts | 3 +- .../app/api/table/[tableId]/restore/route.ts | 3 +- apps/sim/app/api/table/[tableId]/route.ts | 3 +- .../app/api/tools/asana/create-task/route.ts | 4 +- .../app/api/tools/asana/update-task/route.ts | 4 +- .../tools/athena/create-named-query/route.ts | 4 +- .../api/tools/athena/get-named-query/route.ts | 3 +- .../tools/athena/get-query-execution/route.ts | 4 +- .../tools/athena/get-query-results/route.ts | 4 +- .../tools/athena/list-named-queries/route.ts | 4 +- .../athena/list-query-executions/route.ts | 4 +- .../app/api/tools/athena/start-query/route.ts | 3 +- .../app/api/tools/athena/stop-query/route.ts | 3 +- apps/sim/app/api/tools/box/upload/route.ts | 3 +- .../route.ts | 4 +- .../describe-stack-events/route.ts | 4 +- .../cloudformation/describe-stacks/route.ts | 4 +- .../detect-stack-drift/route.ts | 4 +- .../cloudformation/get-template/route.ts | 4 +- .../list-stack-resources/route.ts | 4 +- .../cloudformation/validate-template/route.ts | 4 +- .../confluence/upload-attachment/route.ts | 5 +- .../app/api/tools/crowdstrike/query/route.ts | 3 +- .../tools/cursor/download-artifact/route.ts | 3 +- apps/sim/app/api/tools/custom/route.ts | 3 +- .../api/tools/discord/send-message/route.ts | 5 +- apps/sim/app/api/tools/docusign/route.ts | 3 +- .../sim/app/api/tools/dropbox/upload/route.ts | 3 +- .../app/api/tools/evernote/copy-note/route.ts | 3 +- .../api/tools/evernote/create-note/route.ts | 3 +- .../tools/evernote/create-notebook/route.ts | 3 +- .../api/tools/evernote/create-tag/route.ts | 3 +- .../api/tools/evernote/delete-note/route.ts | 3 +- .../app/api/tools/evernote/get-note/route.ts | 3 +- .../api/tools/evernote/get-notebook/route.ts | 3 +- .../tools/evernote/list-notebooks/route.ts | 3 +- .../app/api/tools/evernote/list-tags/route.ts | 3 +- .../api/tools/evernote/search-notes/route.ts | 3 +- .../api/tools/evernote/update-note/route.ts | 3 +- apps/sim/app/api/tools/extend/parse/route.ts | 3 +- apps/sim/app/api/tools/file/manage/route.ts | 6 +- .../api/tools/github/latest-commit/route.ts | 3 +- .../app/api/tools/gmail/add-label/route.ts | 3 +- apps/sim/app/api/tools/gmail/archive/route.ts | 3 +- apps/sim/app/api/tools/gmail/delete/route.ts | 3 +- apps/sim/app/api/tools/gmail/draft/route.ts | 5 +- .../app/api/tools/gmail/edit-draft/route.ts | 5 +- .../app/api/tools/gmail/mark-read/route.ts | 3 +- .../app/api/tools/gmail/mark-unread/route.ts | 3 +- apps/sim/app/api/tools/gmail/move/route.ts | 3 +- .../app/api/tools/gmail/remove-label/route.ts | 3 +- apps/sim/app/api/tools/gmail/send/route.ts | 5 +- .../app/api/tools/gmail/unarchive/route.ts | 3 +- .../api/tools/google_drive/download/route.ts | 3 +- .../api/tools/google_drive/upload/route.ts | 10 +- .../download-export-file/route.ts | 3 +- .../sim/app/api/tools/imap/mailboxes/route.ts | 3 +- .../api/tools/jira/add-attachment/route.ts | 3 +- apps/sim/app/api/tools/jira/update/route.ts | 4 +- apps/sim/app/api/tools/jira/write/route.ts | 4 +- apps/sim/app/api/tools/jsm/approvals/route.ts | 4 +- apps/sim/app/api/tools/jsm/comment/route.ts | 4 +- apps/sim/app/api/tools/jsm/comments/route.ts | 4 +- apps/sim/app/api/tools/jsm/customers/route.ts | 4 +- .../app/api/tools/jsm/forms/answers/route.ts | 4 +- .../app/api/tools/jsm/forms/attach/route.ts | 4 +- .../sim/app/api/tools/jsm/forms/copy/route.ts | 4 +- .../app/api/tools/jsm/forms/delete/route.ts | 4 +- .../api/tools/jsm/forms/externalise/route.ts | 4 +- apps/sim/app/api/tools/jsm/forms/get/route.ts | 4 +- .../api/tools/jsm/forms/internalise/route.ts | 4 +- .../app/api/tools/jsm/forms/issue/route.ts | 4 +- .../app/api/tools/jsm/forms/reopen/route.ts | 4 +- .../sim/app/api/tools/jsm/forms/save/route.ts | 4 +- .../api/tools/jsm/forms/structure/route.ts | 4 +- .../app/api/tools/jsm/forms/submit/route.ts | 4 +- .../api/tools/jsm/forms/templates/route.ts | 4 +- .../app/api/tools/jsm/organization/route.ts | 4 +- .../app/api/tools/jsm/organizations/route.ts | 4 +- .../app/api/tools/jsm/participants/route.ts | 4 +- apps/sim/app/api/tools/jsm/queues/route.ts | 4 +- apps/sim/app/api/tools/jsm/request/route.ts | 4 +- apps/sim/app/api/tools/jsm/requests/route.ts | 4 +- .../api/tools/jsm/requesttypefields/route.ts | 4 +- .../app/api/tools/jsm/requesttypes/route.ts | 4 +- .../app/api/tools/jsm/servicedesks/route.ts | 4 +- apps/sim/app/api/tools/jsm/sla/route.ts | 4 +- .../sim/app/api/tools/jsm/transition/route.ts | 4 +- .../app/api/tools/jsm/transitions/route.ts | 4 +- .../microsoft-dataverse/upload-file/route.ts | 5 +- .../api/tools/microsoft_excel/sheets/route.ts | 3 +- .../delete_chat_message/route.ts | 3 +- .../microsoft_teams/write_channel/route.ts | 3 +- .../tools/microsoft_teams/write_chat/route.ts | 3 +- apps/sim/app/api/tools/mistral/parse/route.ts | 5 +- .../sim/app/api/tools/mongodb/delete/route.ts | 3 +- .../app/api/tools/mongodb/execute/route.ts | 3 +- .../sim/app/api/tools/mongodb/insert/route.ts | 3 +- .../app/api/tools/mongodb/introspect/route.ts | 3 +- apps/sim/app/api/tools/mongodb/query/route.ts | 3 +- .../sim/app/api/tools/mongodb/update/route.ts | 3 +- apps/sim/app/api/tools/mysql/delete/route.ts | 3 +- apps/sim/app/api/tools/mysql/execute/route.ts | 3 +- apps/sim/app/api/tools/mysql/insert/route.ts | 3 +- .../app/api/tools/mysql/introspect/route.ts | 3 +- apps/sim/app/api/tools/mysql/query/route.ts | 3 +- apps/sim/app/api/tools/mysql/update/route.ts | 3 +- apps/sim/app/api/tools/neo4j/create/route.ts | 3 +- apps/sim/app/api/tools/neo4j/delete/route.ts | 3 +- apps/sim/app/api/tools/neo4j/execute/route.ts | 3 +- .../app/api/tools/neo4j/introspect/route.ts | 3 +- apps/sim/app/api/tools/neo4j/merge/route.ts | 3 +- apps/sim/app/api/tools/neo4j/query/route.ts | 3 +- apps/sim/app/api/tools/neo4j/update/route.ts | 3 +- .../app/api/tools/onedrive/download/route.ts | 3 +- .../app/api/tools/onedrive/upload/route.ts | 9 +- .../tools/onepassword/create-item/route.ts | 3 +- .../tools/onepassword/delete-item/route.ts | 3 +- .../api/tools/onepassword/get-item/route.ts | 3 +- .../api/tools/onepassword/get-vault/route.ts | 3 +- .../api/tools/onepassword/list-items/route.ts | 3 +- .../tools/onepassword/list-vaults/route.ts | 3 +- .../tools/onepassword/replace-item/route.ts | 3 +- .../tools/onepassword/resolve-secret/route.ts | 3 +- .../tools/onepassword/update-item/route.ts | 3 +- apps/sim/app/api/tools/outlook/copy/route.ts | 3 +- .../sim/app/api/tools/outlook/delete/route.ts | 3 +- apps/sim/app/api/tools/outlook/draft/route.ts | 5 +- .../app/api/tools/outlook/mark-read/route.ts | 3 +- .../api/tools/outlook/mark-unread/route.ts | 3 +- apps/sim/app/api/tools/outlook/move/route.ts | 3 +- apps/sim/app/api/tools/outlook/send/route.ts | 5 +- .../api/tools/pipedrive/get-files/route.ts | 3 +- .../app/api/tools/postgresql/delete/route.ts | 3 +- .../app/api/tools/postgresql/execute/route.ts | 3 +- .../app/api/tools/postgresql/insert/route.ts | 3 +- .../api/tools/postgresql/introspect/route.ts | 3 +- .../app/api/tools/postgresql/query/route.ts | 3 +- .../app/api/tools/postgresql/update/route.ts | 3 +- apps/sim/app/api/tools/pulse/parse/route.ts | 3 +- .../api/tools/quiver/image-to-svg/route.ts | 3 +- .../app/api/tools/quiver/text-to-svg/route.ts | 3 +- apps/sim/app/api/tools/rds/delete/route.ts | 3 +- apps/sim/app/api/tools/rds/execute/route.ts | 3 +- apps/sim/app/api/tools/rds/insert/route.ts | 3 +- .../sim/app/api/tools/rds/introspect/route.ts | 3 +- apps/sim/app/api/tools/rds/query/route.ts | 3 +- apps/sim/app/api/tools/rds/update/route.ts | 3 +- apps/sim/app/api/tools/redis/execute/route.ts | 3 +- apps/sim/app/api/tools/reducto/parse/route.ts | 3 +- .../sim/app/api/tools/s3/copy-object/route.ts | 3 +- .../app/api/tools/s3/delete-object/route.ts | 3 +- .../app/api/tools/s3/list-objects/route.ts | 3 +- apps/sim/app/api/tools/s3/put-object/route.ts | 5 +- .../secrets_manager/create-secret/route.ts | 3 +- .../secrets_manager/delete-secret/route.ts | 3 +- .../tools/secrets_manager/get-secret/route.ts | 3 +- .../secrets_manager/list-secrets/route.ts | 3 +- .../secrets_manager/update-secret/route.ts | 3 +- .../app/api/tools/sendgrid/send-mail/route.ts | 5 +- apps/sim/app/api/tools/sftp/delete/route.ts | 3 +- apps/sim/app/api/tools/sftp/download/route.ts | 3 +- apps/sim/app/api/tools/sftp/mkdir/route.ts | 3 +- apps/sim/app/api/tools/sftp/upload/route.ts | 7 +- .../app/api/tools/slack/add-reaction/route.ts | 3 +- .../api/tools/slack/delete-message/route.ts | 3 +- .../sim/app/api/tools/slack/download/route.ts | 3 +- .../api/tools/slack/read-messages/route.ts | 3 +- .../api/tools/slack/remove-reaction/route.ts | 3 +- .../api/tools/slack/send-ephemeral/route.ts | 3 +- .../app/api/tools/slack/send-message/route.ts | 3 +- .../api/tools/slack/update-message/route.ts | 3 +- apps/sim/app/api/tools/smtp/send/route.ts | 4 +- apps/sim/app/api/tools/sqs/send/route.ts | 3 +- .../tools/ssh/check-command-exists/route.ts | 3 +- .../api/tools/ssh/check-file-exists/route.ts | 3 +- .../api/tools/ssh/create-directory/route.ts | 3 +- .../app/api/tools/ssh/delete-file/route.ts | 3 +- .../app/api/tools/ssh/download-file/route.ts | 3 +- .../api/tools/ssh/execute-command/route.ts | 3 +- .../app/api/tools/ssh/execute-script/route.ts | 3 +- .../api/tools/ssh/get-system-info/route.ts | 3 +- .../app/api/tools/ssh/list-directory/route.ts | 3 +- .../app/api/tools/ssh/move-rename/route.ts | 3 +- .../api/tools/ssh/read-file-content/route.ts | 3 +- .../app/api/tools/ssh/upload-file/route.ts | 3 +- .../api/tools/ssh/write-file-content/route.ts | 3 +- .../app/api/tools/stagehand/agent/route.ts | 7 +- .../app/api/tools/stagehand/extract/route.ts | 12 +- apps/sim/app/api/tools/stt/route.ts | 7 +- .../tools/supabase/storage-upload/route.ts | 5 +- .../api/tools/telegram/send-document/route.ts | 3 +- .../sim/app/api/tools/textract/parse/route.ts | 5 +- apps/sim/app/api/tools/tts/route.ts | 3 +- apps/sim/app/api/tools/tts/unified/route.ts | 5 +- .../api/tools/twilio/get-recording/route.ts | 3 +- apps/sim/app/api/tools/video/route.ts | 13 +- .../sim/app/api/tools/vision/analyze/route.ts | 5 +- .../app/api/tools/wordpress/upload/route.ts | 7 +- .../tools/workday/assign-onboarding/route.ts | 3 +- .../app/api/tools/workday/change-job/route.ts | 3 +- .../api/tools/workday/create-prehire/route.ts | 3 +- .../tools/workday/get-compensation/route.ts | 3 +- .../tools/workday/get-organizations/route.ts | 3 +- .../app/api/tools/workday/get-worker/route.ts | 3 +- apps/sim/app/api/tools/workday/hire/route.ts | 3 +- .../api/tools/workday/list-workers/route.ts | 3 +- .../app/api/tools/workday/terminate/route.ts | 3 +- .../api/tools/workday/update-worker/route.ts | 3 +- .../api/tools/zoom/get-recordings/route.ts | 3 +- .../app/api/users/me/usage-limits/route.ts | 6 +- .../v1/admin/workspaces/[id]/import/route.ts | 3 +- apps/sim/app/api/v1/audit-logs/[id]/route.ts | 3 +- apps/sim/app/api/v1/audit-logs/route.ts | 3 +- apps/sim/app/api/v1/files/route.ts | 3 +- .../app/api/v1/knowledge/search/route.test.ts | 4 +- apps/sim/app/api/v1/workflows/[id]/route.ts | 3 +- apps/sim/app/api/v1/workflows/route.ts | 3 +- apps/sim/app/api/webhooks/agentmail/route.ts | 7 +- .../api/webhooks/cleanup/idempotency/route.ts | 3 +- .../app/api/webhooks/poll/[provider]/route.ts | 3 +- apps/sim/app/api/webhooks/route.ts | 7 +- .../api/workflows/[id]/autolayout/route.ts | 3 +- .../app/api/workflows/[id]/deploy/route.ts | 8 +- .../app/api/workflows/[id]/execute/route.ts | 14 +- .../api/workflows/[id]/form/status/route.ts | 5 +- .../app/api/workflows/[id]/restore/route.ts | 3 +- .../app/api/workflows/[id]/variables/route.ts | 3 +- .../workspaces/[id]/api-keys/[keyId]/route.ts | 5 +- .../app/api/workspaces/[id]/api-keys/route.ts | 7 +- .../api/workspaces/[id]/byok-keys/route.ts | 7 +- .../[id]/files/[fileId]/download/route.ts | 3 +- .../[id]/files/[fileId]/restore/route.ts | 3 +- .../workspaces/[id]/files/[fileId]/route.ts | 5 +- .../workspaces/[id]/files/presigned/route.ts | 3 +- .../workspaces/[id]/files/register/route.ts | 3 +- .../app/api/workspaces/[id]/files/route.ts | 5 +- .../app/api/workspaces/[id]/inbox/route.ts | 5 +- apps/sim/app/form/[identifier]/form.tsx | 7 +- .../[workflowId]/[executionId]/page.tsx | 2 +- apps/sim/app/unsubscribe/unsubscribe.tsx | 5 +- .../[workspaceId]/home/hooks/use-chat.ts | 12 +- .../[workspaceId]/knowledge/[id]/base.tsx | 4 +- .../rename-document-modal.tsx | 3 +- .../create-base-modal/create-base-modal.tsx | 3 +- .../edit-knowledge-base-modal.tsx | 3 +- .../knowledge/hooks/use-knowledge-upload.ts | 4 +- .../components/line-chart/line-chart.tsx | 3 +- .../slack-channel-selector.tsx | 3 +- .../notifications/notifications.tsx | 3 +- .../create-schedule-modal/schedule-modal.tsx | 5 +- .../settings/components/admin/admin.tsx | 3 +- .../create-api-key-modal.tsx | 4 +- .../settings/components/byok/byok.tsx | 3 +- .../components/custom-tools/custom-tools.tsx | 3 +- .../components/inbox/inbox-settings-tab.tsx | 7 +- .../integrations/integrations-manager.tsx | 11 +- .../integrations/service-account-form.tsx | 3 +- .../mcp-server-form-modal.tsx | 7 +- .../settings/components/mcp/mcp.tsx | 3 +- .../components/secrets/secrets-manager.tsx | 24 +-- .../skills/components/skill-import.tsx | 5 +- .../settings/components/skills/skills.tsx | 3 +- .../components/subscription/subscription.tsx | 21 ++- .../workflow-mcp-servers.tsx | 3 +- .../hooks/use-profile-picture-upload.ts | 4 +- .../components/row-modal/row-modal.tsx | 5 +- .../import-csv-dialog/import-csv-dialog.tsx | 5 +- .../deploy-modal/components/form/form.tsx | 3 +- .../general/components/api-info-modal.tsx | 3 +- .../deploy/hooks/use-predeploy-checks.ts | 3 +- .../components/combobox/combobox.tsx | 3 +- .../components/dropdown/dropdown.tsx | 3 +- .../components/file-upload/file-upload.tsx | 19 +-- .../table-selector/table-selector.tsx | 2 +- .../custom-tool-modal/custom-tool-modal.tsx | 5 +- .../sub-block/hooks/use-sub-block-value.ts | 4 +- .../workflow-block/workflow-block.tsx | 3 +- .../w/[workflowId]/hooks/use-auto-layout.ts | 3 +- .../hooks/use-workflow-execution.ts | 12 +- .../w/[workflowId]/utils/auto-layout-utils.ts | 5 +- .../hooks/use-workspace-logo-upload.ts | 4 +- .../workspace-notification-delivery.ts | 6 +- apps/sim/blocks/blocks/apollo.ts | 3 +- apps/sim/blocks/blocks/mongodb.ts | 3 +- apps/sim/blocks/blocks/mysql.ts | 3 +- apps/sim/blocks/blocks/neo4j.ts | 4 +- apps/sim/blocks/blocks/postgresql.ts | 3 +- apps/sim/blocks/blocks/rds.ts | 4 +- apps/sim/blocks/blocks/sqs.ts | 4 +- apps/sim/blocks/blocks/supabase.ts | 11 +- apps/sim/connectors/airtable/airtable.ts | 4 +- apps/sim/connectors/asana/asana.ts | 4 +- apps/sim/connectors/discord/discord.ts | 4 +- apps/sim/connectors/dropbox/dropbox.ts | 4 +- apps/sim/connectors/fireflies/fireflies.ts | 4 +- apps/sim/connectors/github/github.ts | 6 +- apps/sim/connectors/gmail/gmail.ts | 4 +- .../google-calendar/google-calendar.ts | 3 +- .../connectors/google-drive/google-drive.ts | 4 +- .../connectors/google-sheets/google-sheets.ts | 4 +- apps/sim/connectors/hubspot/hubspot.ts | 3 +- apps/sim/connectors/intercom/intercom.ts | 4 +- apps/sim/connectors/linear/linear.ts | 4 +- .../microsoft-teams/microsoft-teams.ts | 4 +- apps/sim/connectors/notion/notion.ts | 4 +- apps/sim/connectors/onedrive/onedrive.ts | 4 +- apps/sim/connectors/outlook/outlook.ts | 4 +- apps/sim/connectors/reddit/reddit.ts | 4 +- apps/sim/connectors/sharepoint/sharepoint.ts | 4 +- apps/sim/connectors/webflow/webflow.ts | 4 +- apps/sim/connectors/wordpress/wordpress.ts | 4 +- apps/sim/ee/sso/components/sso-settings.tsx | 3 +- apps/sim/executor/execution/engine.test.ts | 23 +-- .../handlers/workflow/workflow-handler.ts | 3 +- apps/sim/executor/utils/json.ts | 3 +- apps/sim/hooks/queries/mcp.ts | 6 +- apps/sim/hooks/queries/providers.ts | 3 +- apps/sim/hooks/use-execution-stream.ts | 3 +- apps/sim/instrumentation-client.ts | 3 +- .../api/contracts/tools/databases/shared.ts | 3 +- apps/sim/lib/billing/client/upgrade.ts | 5 +- apps/sim/lib/billing/credits/purchase.ts | 3 +- .../lib/billing/organizations/membership.ts | 3 +- apps/sim/lib/billing/stripe-payment-method.ts | 3 +- .../lib/billing/webhooks/outbox-handlers.ts | 3 +- apps/sim/lib/copilot/async-runs/repository.ts | 3 +- apps/sim/lib/copilot/chat/post.ts | 11 +- apps/sim/lib/copilot/request/go/stream.ts | 5 +- .../copilot/request/handlers/handlers.test.ts | 29 ++-- apps/sim/lib/copilot/request/handlers/tool.ts | 4 +- .../lib/copilot/request/lifecycle/headless.ts | 3 +- .../lib/copilot/request/lifecycle/start.ts | 13 +- .../lib/copilot/request/session/contract.ts | 3 +- .../lib/copilot/request/session/recovery.ts | 3 +- .../handlers/management/manage-custom-tool.ts | 4 +- .../handlers/management/manage-mcp-tool.ts | 4 +- .../tools/handlers/management/manage-skill.ts | 4 +- .../tools/handlers/materialize-file.ts | 4 +- apps/sim/lib/copilot/tools/handlers/vfs.ts | 10 +- .../files/download-to-workspace-file.ts | 3 +- .../tools/server/files/edit-content.ts | 4 +- .../tools/server/files/workspace-file.ts | 7 +- .../tools/server/image/generate-image.ts | 4 +- .../tools/server/knowledge/knowledge-base.ts | 4 +- .../visualization/generate-visualization.ts | 3 +- .../server/workflow/edit-workflow/engine.ts | 2 +- apps/sim/lib/copilot/vfs/serializers.ts | 5 +- apps/sim/lib/core/config/redis.ts | 3 +- apps/sim/lib/core/idempotency/cleanup.ts | 6 +- apps/sim/lib/core/idempotency/service.ts | 3 +- apps/sim/lib/core/security/csp.test.ts | 4 +- apps/sim/lib/core/security/encryption.ts | 3 +- apps/sim/lib/core/security/redaction.test.ts | 2 +- apps/sim/lib/core/telemetry.ts | 4 +- apps/sim/lib/core/utils/logging.ts | 6 +- apps/sim/lib/core/utils/with-route-handler.ts | 3 +- .../lib/data-drains/destinations/bigquery.ts | 3 +- .../lib/data-drains/destinations/datadog.ts | 8 +- apps/sim/lib/data-drains/destinations/gcs.ts | 3 +- .../lib/data-drains/destinations/snowflake.ts | 7 +- .../sim/lib/data-drains/destinations/utils.ts | 67 -------- .../lib/data-drains/destinations/webhook.ts | 7 +- apps/sim/lib/environment/utils.ts | 3 +- apps/sim/lib/execution/event-buffer.ts | 3 +- apps/sim/lib/execution/isolated-vm.test.ts | 5 +- apps/sim/lib/execution/isolated-vm.ts | 13 +- apps/sim/lib/execution/payloads/store.ts | 3 +- apps/sim/lib/file-parsers/json-parser.ts | 5 +- apps/sim/lib/file-parsers/yaml-parser.ts | 5 +- .../lib/knowledge/connectors/sync-engine.ts | 3 +- .../knowledge/documents/document-processor.ts | 4 +- apps/sim/lib/knowledge/documents/service.ts | 10 +- apps/sim/lib/knowledge/documents/utils.ts | 3 +- apps/sim/lib/mcp/client.ts | 3 +- apps/sim/lib/mcp/service.ts | 7 +- apps/sim/lib/mcp/storage/memory-cache.test.ts | 7 +- apps/sim/lib/messaging/email/mailer.ts | 3 +- apps/sim/lib/messaging/sms/service.ts | 5 +- apps/sim/lib/mothership/inbox/executor.ts | 11 +- apps/sim/lib/mothership/inbox/response.ts | 3 +- apps/sim/lib/pptx-renderer/core/viewer.ts | 5 +- apps/sim/lib/table/constants.ts | 11 +- .../execution/execution-file-manager.ts | 9 +- .../lib/uploads/contexts/execution/utils.ts | 3 +- .../workspace/workspace-file-manager.ts | 18 +- .../lib/uploads/utils/file-utils.server.ts | 3 +- apps/sim/lib/webhooks/polling/gmail.ts | 7 +- .../lib/webhooks/polling/google-calendar.ts | 3 +- apps/sim/lib/webhooks/polling/google-drive.ts | 3 +- .../sim/lib/webhooks/polling/google-sheets.ts | 3 +- apps/sim/lib/webhooks/polling/imap.ts | 3 +- apps/sim/lib/webhooks/polling/outlook.ts | 3 +- apps/sim/lib/webhooks/polling/rss.ts | 5 +- apps/sim/lib/webhooks/providers/stripe.ts | 3 +- apps/sim/lib/webhooks/utils.server.ts | 5 +- apps/sim/lib/workflows/autolayout/index.ts | 5 +- apps/sim/lib/workflows/autolayout/targeted.ts | 2 +- apps/sim/lib/workflows/colors.ts | 4 +- .../workflows/comparison/resolve-values.ts | 5 +- .../credentials/credential-extractor.ts | 2 +- apps/sim/lib/workflows/diff/diff-engine.ts | 10 +- .../lib/workflows/executor/execution-core.ts | 3 +- .../lib/workflows/operations/import-export.ts | 7 +- .../lib/workflows/persistence/duplicate.ts | 4 +- .../lib/workflows/persistence/utils.test.ts | 2 +- apps/sim/lib/workflows/persistence/utils.ts | 13 +- apps/sim/lib/workflows/schedules/deploy.ts | 3 +- apps/sim/lib/workflows/schedules/utils.ts | 4 +- .../sim/lib/workflows/schedules/validation.ts | 3 +- apps/sim/lib/workflows/streaming/streaming.ts | 3 +- apps/sim/lib/workspaces/colors.ts | 4 +- apps/sim/lib/workspaces/naming.ts | 3 +- apps/sim/providers/anthropic/core.ts | 6 +- apps/sim/providers/anthropic/utils.ts | 3 +- apps/sim/providers/azure-openai/index.ts | 4 +- apps/sim/providers/bedrock/index.ts | 4 +- apps/sim/providers/bedrock/utils.ts | 3 +- apps/sim/providers/cerebras/index.ts | 4 +- apps/sim/providers/deepseek/index.ts | 4 +- apps/sim/providers/fireworks/index.ts | 4 +- apps/sim/providers/gemini/core.ts | 4 +- apps/sim/providers/groq/index.ts | 4 +- apps/sim/providers/mistral/index.ts | 4 +- apps/sim/providers/ollama/index.ts | 6 +- apps/sim/providers/openai/core.ts | 4 +- apps/sim/providers/openrouter/index.ts | 4 +- apps/sim/providers/registry.ts | 3 +- apps/sim/providers/utils.ts | 5 +- apps/sim/providers/vllm/index.ts | 6 +- apps/sim/providers/xai/index.ts | 4 +- apps/sim/stores/variables/store.ts | 3 +- apps/sim/stores/workflows/registry/store.ts | 11 +- apps/sim/stores/workflows/registry/utils.ts | 6 +- apps/sim/stores/workflows/utils.ts | 6 +- apps/sim/stores/workflows/workflow/store.ts | 8 +- apps/sim/tools/databricks/run_job.ts | 5 +- apps/sim/tools/emailbison/utils.ts | 3 +- apps/sim/tools/gmail/utils.ts | 3 +- apps/sim/tools/google_slides/add_image.ts | 3 +- apps/sim/tools/google_slides/add_slide.ts | 3 +- apps/sim/tools/google_slides/create_shape.ts | 3 +- apps/sim/tools/google_slides/create_table.ts | 3 +- apps/sim/tools/index.ts | 8 +- apps/sim/tools/langsmith/create_runs_batch.ts | 3 +- apps/sim/tools/linkedin/share_post.ts | 3 +- apps/sim/tools/mistral/parser.ts | 3 +- apps/sim/tools/openai/image.ts | 2 +- apps/sim/tools/parallel/deep_research.ts | 3 +- apps/sim/tools/s3/get_object.ts | 3 +- apps/sim/tools/salesforce/run_report.ts | 3 +- apps/sim/tools/supabase/introspect.ts | 3 +- apps/sim/tools/tinybird/query.ts | 6 +- apps/sim/tools/trello/add_comment.ts | 3 +- apps/sim/tools/trello/create_card.ts | 3 +- apps/sim/tools/trello/get_actions.ts | 3 +- apps/sim/tools/trello/list_cards.ts | 3 +- apps/sim/tools/trello/list_lists.ts | 3 +- apps/sim/tools/trello/update_card.ts | 3 +- apps/sim/tools/workday/soap.ts | 3 +- biome.json | 11 +- package.json | 1 + packages/audit/src/log.test.ts | 3 +- packages/db/scripts/backfill-api-key-hash.ts | 7 +- .../db/scripts/deregister-sso-provider.ts | 3 +- .../scripts/migrate-block-api-keys-to-byok.ts | 5 +- packages/db/scripts/register-sso-provider.ts | 5 +- packages/db/scripts/seed-stress-test-users.ts | 13 +- .../testing/src/builders/execution.builder.ts | 3 +- .../testing/src/builders/workflow.builder.ts | 3 +- .../testing/src/factories/block.factory.ts | 3 +- packages/testing/src/factories/dag.factory.ts | 3 +- .../testing/src/factories/edge.factory.ts | 4 +- .../src/factories/execution.factory.ts | 3 +- .../src/factories/tool-responses.factory.ts | 4 +- .../testing/src/factories/user.factory.ts | 11 +- packages/testing/src/mocks/socket.mock.ts | 3 +- .../src/mocks/terminal-console.mock.ts | 3 +- packages/ts-sdk/src/index.ts | 1 + packages/utils/package.json | 16 ++ packages/utils/src/errors.ts | 14 ++ packages/utils/src/index.ts | 15 +- packages/utils/src/object.ts | 24 +++ packages/utils/src/random.ts | 78 +++++++++ packages/utils/src/retry.ts | 59 +++++++ packages/utils/src/string.ts | 13 ++ scripts/check-utils-enforcement.ts | 154 ++++++++++++++++++ 534 files changed, 1679 insertions(+), 1050 deletions(-) create mode 100644 packages/utils/src/object.ts create mode 100644 packages/utils/src/random.ts create mode 100644 packages/utils/src/retry.ts create mode 100644 packages/utils/src/string.ts create mode 100644 scripts/check-utils-enforcement.ts diff --git a/.claude/rules/global.md b/.claude/rules/global.md index b5bc94ec1b..4d47c82f60 100644 --- a/.claude/rules/global.md +++ b/.claude/rules/global.md @@ -36,22 +36,31 @@ const tiny = generateShortId(8) ## Common Utilities Use shared helpers from `@sim/utils` instead of writing inline implementations: -- `sleep(ms)` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))` -- `toError(value)` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))` -- `toError(value).message` — get error message safely. Never write `e instanceof Error ? e.message : String(e)` +- `sleep(ms)` from `@sim/utils/helpers` — async delay. Never write `new Promise(resolve => setTimeout(resolve, ms))` +- `toError(value)` from `@sim/utils/errors` — normalize unknown caught values to `Error`. Never write `e instanceof Error ? e : new Error(String(e))` +- `getErrorMessage(value, fallback?)` from `@sim/utils/errors` — extract error message string. Never write `e instanceof Error ? e.message : 'fallback'` +- `structuredClone(value)` — built-in deep clone, no import needed. Never write `JSON.parse(JSON.stringify(obj))` +- `omit(obj, keys)` from `@sim/utils/object` — remove keys from object +- `filterUndefined(obj)` from `@sim/utils/object` — strip undefined-valued keys. Never write `Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined))` +- `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — safe string truncation with ellipsis +- `backoffWithJitter(attempt, retryAfterMs, options?)` from `@sim/utils/retry` — exponential backoff with jitter +- `parseRetryAfter(header)` from `@sim/utils/retry` — parse HTTP `Retry-After` header to milliseconds ```typescript // ✗ Bad await new Promise(resolve => setTimeout(resolve, 1000)) -const msg = error instanceof Error ? error.message : String(error) -const err = error instanceof Error ? error : new Error(String(error)) +const msg = error instanceof Error ? error.message : 'Unknown error' +const clone = JSON.parse(JSON.stringify(obj)) +const filtered = Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) // ✓ Good import { sleep } from '@sim/utils/helpers' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' +import { filterUndefined } from '@sim/utils/object' await sleep(1000) -const msg = toError(error).message -const err = toError(error) +const msg = getErrorMessage(error, 'Unknown error') +const clone = structuredClone(obj) +const filtered = filterUndefined(obj) ``` ## Package Manager diff --git a/CLAUDE.md b/CLAUDE.md index 9807d221ca..1b6a602ef2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,14 @@ You are a professional software engineer. All code must follow best practices: a - **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments - **Styling**: Never update global styles. Keep all styling local to components - **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@sim/utils/id` -- **Common Utilities**: Use shared helpers from `@sim/utils` instead of inline implementations. `sleep(ms)` from `@sim/utils/helpers` for delays, `toError(e)` from `@sim/utils/errors` to normalize caught values. +- **Common Utilities**: Use shared helpers from `@sim/utils` instead of inline implementations: + - `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))` + - `toError(e)` from `@sim/utils/errors` — normalize caught values to `Error` + - `getErrorMessage(e, fallback?)` from `@sim/utils/errors` — extract message string from unknown caught value; never write `e instanceof Error ? e.message : 'fallback'` + - `structuredClone(value)` — built-in deep clone; never `JSON.parse(JSON.stringify(...))` + - `omit(obj, keys)` / `filterUndefined(obj)` from `@sim/utils/object` — object trimming; never `Object.fromEntries(Object.entries(...).filter(...))` + - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis + - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — shared retry pacing; never reimplement exponential backoff inline - **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx` ## Architecture diff --git a/apps/realtime/src/database/operations.ts b/apps/realtime/src/database/operations.ts index 4904daccb8..8e301acf9b 100644 --- a/apps/realtime/src/database/operations.ts +++ b/apps/realtime/src/database/operations.ts @@ -13,6 +13,7 @@ import { VARIABLE_OPERATIONS, WORKFLOW_OPERATIONS, } from '@sim/realtime-protocol/constants' +import { randomFloat } from '@sim/utils/random' import { getActiveWorkflowContext } from '@sim/workflow-authz' import { loadWorkflowFromNormalizedTablesRaw } from '@sim/workflow-persistence/load' import { mergeSubBlockValues } from '@sim/workflow-persistence/subblocks' @@ -204,7 +205,7 @@ export async function persistWorkflowOperation(workflowId: string, operation: an throw new Error(`Workflow ${workflowId} is archived or unavailable`) } - if (op === BLOCK_OPERATIONS.UPDATE_POSITION && Math.random() < 0.01) { + if (op === BLOCK_OPERATIONS.UPDATE_POSITION && randomFloat() < 0.01) { logger.debug('Socket DB operation sample:', { operation: op, target, diff --git a/apps/realtime/src/handlers/operations.ts b/apps/realtime/src/handlers/operations.ts index 1884c90781..3fa4ac0f8e 100644 --- a/apps/realtime/src/handlers/operations.ts +++ b/apps/realtime/src/handlers/operations.ts @@ -9,6 +9,7 @@ import { WORKFLOW_OPERATIONS, } from '@sim/realtime-protocol/constants' import { WorkflowOperationSchema } from '@sim/realtime-protocol/schemas' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { assertWorkflowMutable, WorkflowLockedError } from '@sim/workflow-authz' import { ZodError } from 'zod' @@ -205,7 +206,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager if (operationId) { socket.emit('operation-failed', { operationId, - error: error instanceof Error ? error.message : 'Database persistence failed', + error: getErrorMessage(error, 'Database persistence failed'), retryable: true, }) } @@ -247,7 +248,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager if (operationId) { socket.emit('operation-failed', { operationId, - error: error instanceof Error ? error.message : 'Database persistence failed', + error: getErrorMessage(error, 'Database persistence failed'), retryable: true, }) } @@ -587,7 +588,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager }) } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') if (operationId) { socket.emit('operation-failed', { diff --git a/apps/realtime/src/handlers/subblocks.ts b/apps/realtime/src/handlers/subblocks.ts index 4650f8487c..a54e3eb99e 100644 --- a/apps/realtime/src/handlers/subblocks.ts +++ b/apps/realtime/src/handlers/subblocks.ts @@ -2,6 +2,7 @@ import { db } from '@sim/db' import { workflow, workflowBlocks } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { SUBBLOCK_OPERATIONS } from '@sim/realtime-protocol/constants' +import { getErrorMessage } from '@sim/utils/errors' import { assertWorkflowMutable, WorkflowLockedError } from '@sim/workflow-authz' import { isWorkflowBlockProtected } from '@sim/workflow-types/workflow' import { and, eq } from 'drizzle-orm' @@ -208,7 +209,7 @@ export function setupSubblocksHandlers(socket: AuthenticatedSocket, roomManager: } catch (error) { logger.error('Error handling subblock update:', error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') if (operationId) { socket.emit('operation-failed', { @@ -360,7 +361,7 @@ async function flushSubblockUpdate( pending.opToSocket.forEach((socketId, opId) => { io.to(socketId).emit('operation-failed', { operationId: opId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), retryable: true, }) }) diff --git a/apps/realtime/src/handlers/variables.ts b/apps/realtime/src/handlers/variables.ts index 5bde4c326d..b7c7a529c8 100644 --- a/apps/realtime/src/handlers/variables.ts +++ b/apps/realtime/src/handlers/variables.ts @@ -2,6 +2,7 @@ import { db } from '@sim/db' import { workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { VARIABLE_OPERATIONS } from '@sim/realtime-protocol/constants' +import { getErrorMessage } from '@sim/utils/errors' import { assertWorkflowMutable, WorkflowLockedError } from '@sim/workflow-authz' import { eq } from 'drizzle-orm' import type { AuthenticatedSocket } from '@/middleware/auth' @@ -195,7 +196,7 @@ export function setupVariablesHandlers(socket: AuthenticatedSocket, roomManager: } catch (error) { logger.error('Error handling variable update:', error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') if (operationId) { socket.emit('operation-failed', { @@ -326,7 +327,7 @@ async function flushVariableUpdate( pending.opToSocket.forEach((socketId, opId) => { io.to(socketId).emit('operation-failed', { operationId: opId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), retryable: true, }) }) diff --git a/apps/realtime/src/index.test.ts b/apps/realtime/src/index.test.ts index c00592e0d6..f2d6d8e686 100644 --- a/apps/realtime/src/index.test.ts +++ b/apps/realtime/src/index.test.ts @@ -5,6 +5,7 @@ */ import { createServer, request as httpRequest } from 'http' import { createMockLogger } from '@sim/testing' +import { randomInt } from '@sim/utils/random' import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { createSocketIOServer } from '@/config/socket' import { MemoryRoomManager } from '@/rooms' @@ -95,7 +96,7 @@ describe('Socket Server Index Integration', () => { }) beforeEach(async () => { - PORT = 3333 + Math.floor(Math.random() * 1000) + PORT = 3333 + randomInt(0, 1000) httpServer = createServer() @@ -120,7 +121,7 @@ describe('Socket Server Index Integration', () => { httpServer.on('error', (err: any) => { clearTimeout(timeout) if (err.code === 'EADDRINUSE') { - PORT = 3333 + Math.floor(Math.random() * 1000) + PORT = 3333 + randomInt(0, 1000) httpServer.close(() => { httpServer.listen(PORT, '0.0.0.0', () => { resolve() diff --git a/apps/sim/app/(auth)/login/login-form.tsx b/apps/sim/app/(auth)/login/login-form.tsx index 17a2dd4a0e..fb32e2decd 100644 --- a/apps/sim/app/(auth)/login/login-form.tsx +++ b/apps/sim/app/(auth)/login/login-form.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Eye, EyeOff } from 'lucide-react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' @@ -292,8 +293,7 @@ export default function LoginPage({ }, }) } catch (requestError) { - let errorMessage = - requestError instanceof Error ? requestError.message : 'Failed to request password reset' + let errorMessage = getErrorMessage(requestError, 'Failed to request password reset') if ( errorMessage.includes('Invalid body parameters') || @@ -325,7 +325,7 @@ export default function LoginPage({ logger.error('Error requesting password reset:', { error }) setResetStatus({ type: 'error', - message: error instanceof Error ? error.message : 'Failed to request password reset', + message: getErrorMessage(error, 'Failed to request password reset'), }) } finally { setIsSubmittingReset(false) diff --git a/apps/sim/app/(auth)/reset-password/reset-password-content.tsx b/apps/sim/app/(auth)/reset-password/reset-password-content.tsx index 337120d631..bdcf335220 100644 --- a/apps/sim/app/(auth)/reset-password/reset-password-content.tsx +++ b/apps/sim/app/(auth)/reset-password/reset-password-content.tsx @@ -2,6 +2,7 @@ import { Suspense, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' import { requestJson } from '@/lib/api/client/request' @@ -53,7 +54,7 @@ function ResetPasswordContent() { logger.error('Error resetting password:', { error }) setStatusMessage({ type: 'error', - text: error instanceof Error ? error.message : 'Failed to reset password', + text: getErrorMessage(error, 'Failed to reset password'), }) } finally { setIsSubmitting(false) diff --git a/apps/sim/app/api/a2a/serve/[agentId]/route.ts b/apps/sim/app/api/a2a/serve/[agentId]/route.ts index a8954abeb3..2a246c7cb3 100644 --- a/apps/sim/app/api/a2a/serve/[agentId]/route.ts +++ b/apps/sim/app/api/a2a/serve/[agentId]/route.ts @@ -2,6 +2,7 @@ import type { Artifact, Message, PushNotificationConfig, TaskState } from '@a2a- import { db } from '@sim/db' import { a2aAgent, a2aPushNotificationConfig, a2aTask, workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq, isNull } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -1394,7 +1395,7 @@ async function handleTaskResubscribe( logger.error('Error during SSE poll:', error) sendEvent('error', { code: A2A_ERROR_CODES.INTERNAL_ERROR, - message: error instanceof Error ? error.message : 'Polling failed', + message: getErrorMessage(error, 'Polling failed'), }) cleanup() try { diff --git a/apps/sim/app/api/admin/mothership/route.ts b/apps/sim/app/api/admin/mothership/route.ts index 7deafb3ad1..a3a022f9d8 100644 --- a/apps/sim/app/api/admin/mothership/route.ts +++ b/apps/sim/app/api/admin/mothership/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { settings, user } from '@sim/db/schema' +import { getErrorMessage } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { adminMothershipQuerySchema } from '@/lib/api/contracts/mothership-tasks' @@ -109,7 +110,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => { } catch (error) { return NextResponse.json( { - error: `Failed to reach mothership (${environment}): ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to reach mothership (${environment}): ${getErrorMessage(error, 'Unknown error')}`, }, { status: 502 } ) @@ -165,7 +166,7 @@ export const GET = withRouteHandler(async (req: NextRequest) => { } catch (error) { return NextResponse.json( { - error: `Failed to reach mothership (${environment}): ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to reach mothership (${environment}): ${getErrorMessage(error, 'Unknown error')}`, }, { status: 502 } ) diff --git a/apps/sim/app/api/audit-logs/route.ts b/apps/sim/app/api/audit-logs/route.ts index 802b1f0c1c..9dcf5977a5 100644 --- a/apps/sim/app/api/audit-logs/route.ts +++ b/apps/sim/app/api/audit-logs/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { listAuditLogsContract } from '@/lib/api/contracts/audit-logs' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -78,7 +79,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { nextCursor, }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Audit logs fetch error', { error: message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/auth/oauth/token/route.ts b/apps/sim/app/api/auth/oauth/token/route.ts index adc517ceff..d7d1b9cd9f 100644 --- a/apps/sim/app/api/auth/oauth/token/route.ts +++ b/apps/sim/app/api/auth/oauth/token/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { oauthTokenGetContract, @@ -97,7 +98,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json({ accessToken }, { status: 200 }) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to get OAuth token' + const message = getErrorMessage(error, 'Failed to get OAuth token') logger.warn(`[${requestId}] OAuth token error: ${message}`) return NextResponse.json({ error: message }, { status: 403 }) } diff --git a/apps/sim/app/api/auth/sso/register/route.ts b/apps/sim/app/api/auth/sso/register/route.ts index a68c830f0e..5b285a5bd2 100644 --- a/apps/sim/app/api/auth/sso/register/route.ts +++ b/apps/sim/app/api/auth/sso/register/route.ts @@ -1,5 +1,6 @@ import { db, member, ssoProvider } from '@sim/db' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { ssoRegistrationContract } from '@/lib/api/contracts/auth' @@ -249,7 +250,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error('Error fetching OIDC discovery document', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), discoveryUrl, }) return NextResponse.json( @@ -427,7 +428,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error('Failed to register SSO provider', { error, - errorMessage: error instanceof Error ? error.message : 'Unknown error', + errorMessage: getErrorMessage(error, 'Unknown error'), errorStack: error instanceof Error ? error.stack : undefined, errorDetails: JSON.stringify(error), }) @@ -435,7 +436,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { error: 'Failed to register SSO provider', - details: error instanceof Error ? error.message : 'Unknown error', + details: getErrorMessage(error, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/billing/switch-plan/route.ts b/apps/sim/app/api/billing/switch-plan/route.ts index 15e523b26b..33a9f40a08 100644 --- a/apps/sim/app/api/billing/switch-plan/route.ts +++ b/apps/sim/app/api/billing/switch-plan/route.ts @@ -1,7 +1,7 @@ import { db } from '@sim/db' import { subscription as subscriptionTable } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { billingSwitchPlanContract } from '@/lib/api/contracts/subscription' @@ -181,7 +181,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { error: toError(error).message, }) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to switch plan' }, + { error: getErrorMessage(error, 'Failed to switch plan') }, { status: 500 } ) } diff --git a/apps/sim/app/api/chat/manage/[id]/route.ts b/apps/sim/app/api/chat/manage/[id]/route.ts index f74a0e0409..0115be7099 100644 --- a/apps/sim/app/api/chat/manage/[id]/route.ts +++ b/apps/sim/app/api/chat/manage/[id]/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { chat } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { chatIdParamsSchema, updateChatContract } from '@/lib/api/contracts/chats' @@ -20,10 +21,6 @@ export const maxDuration = 120 const logger = createLogger('ChatDetailAPI') -function getErrorMessage(error: unknown, fallback: string): string { - return error instanceof Error ? error.message : fallback -} - /** * GET endpoint to fetch a specific chat deployment by ID */ diff --git a/apps/sim/app/api/chat/route.ts b/apps/sim/app/api/chat/route.ts index ca1afd1b16..ca1f8019fe 100644 --- a/apps/sim/app/api/chat/route.ts +++ b/apps/sim/app/api/chat/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { createChatContract } from '@/lib/api/contracts/chats' @@ -13,10 +14,6 @@ import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/ const logger = createLogger('ChatAPI') -function getErrorMessage(error: unknown, fallback: string): string { - return error instanceof Error ? error.message : fallback -} - export const GET = withRouteHandler(async (_request: NextRequest) => { try { const session = await getSession() diff --git a/apps/sim/app/api/copilot/chat/abort/route.ts b/apps/sim/app/api/copilot/chat/abort/route.ts index 29ec7c2f3b..2541613747 100644 --- a/apps/sim/app/api/copilot/chat/abort/route.ts +++ b/apps/sim/app/api/copilot/chat/abort/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { copilotChatAbortBodySchema } from '@/lib/api/contracts/copilot' import { validationErrorResponse } from '@/lib/api/server' @@ -36,7 +37,7 @@ export const POST = withRouteHandler((request: NextRequest) => const body = await request.json().catch((err) => { logger.warn('Abort request body parse failed; continuing with empty object', { - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) return {} }) @@ -61,7 +62,7 @@ export const POST = withRouteHandler((request: NextRequest) => const run = await getLatestRunForStream(streamId, authenticatedUserId).catch((err) => { logger.warn('getLatestRunForStream failed while resolving chatId for abort', { streamId, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) return null }) @@ -115,7 +116,7 @@ export const POST = withRouteHandler((request: NextRequest) => } catch (err) { logger.warn('Explicit abort marker request failed after local abort', { streamId, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) } rootSpan.setAttribute(TraceAttr.CopilotAbortGoMarkerOk, goAbortOk) diff --git a/apps/sim/app/api/copilot/chat/stream/route.ts b/apps/sim/app/api/copilot/chat/stream/route.ts index 7cc61cb644..bd7f546568 100644 --- a/apps/sim/app/api/copilot/chat/stream/route.ts +++ b/apps/sim/app/api/copilot/chat/stream/route.ts @@ -1,5 +1,6 @@ import { type Context, context as otelContext, type Span, trace } from '@opentelemetry/api' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { type NextRequest, NextResponse } from 'next/server' import { copilotChatStreamContract } from '@/lib/api/contracts/copilot' @@ -199,7 +200,7 @@ async function handleResumeRequestBody({ const run = await getLatestRunForStream(streamId, authenticatedUserId).catch((err) => { logger.warn('Failed to fetch latest run for stream', { streamId, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) return null }) @@ -224,7 +225,7 @@ async function handleResumeRequestBody({ readFilePreviewSessions(streamId).catch((error) => { logger.warn('Failed to read preview sessions for stream batch', { streamId, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) return [] }), @@ -395,7 +396,7 @@ async function handleResumeRequestBody({ (err) => { logger.warn('Failed to poll latest run for stream', { streamId, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) return null } @@ -452,7 +453,7 @@ async function handleResumeRequestBody({ if (!controllerClosed && !request.signal.aborted) { logger.warn('Stream replay failed', { streamId, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) emitTerminalIfMissing(MothershipStreamV1CompletionStatus.error, { message: 'The stream replay failed before completion.', diff --git a/apps/sim/app/api/copilot/confirm/route.ts b/apps/sim/app/api/copilot/confirm/route.ts index 58f3435b4f..1dd5bd98a1 100644 --- a/apps/sim/app/api/copilot/confirm/route.ts +++ b/apps/sim/app/api/copilot/confirm/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { copilotConfirmContract } from '@/lib/api/contracts/copilot' import { parseRequest, validationErrorResponse } from '@/lib/api/server' @@ -150,7 +150,7 @@ export const POST = withRouteHandler((req: NextRequest) => { const existing = await getAsyncToolCall(toolCallId).catch((err) => { logger.warn('Failed to fetch async tool call', { toolCallId, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) return null }) @@ -165,7 +165,7 @@ export const POST = withRouteHandler((req: NextRequest) => { const run = await getRunSegment(existing.runId).catch((err) => { logger.warn('Failed to fetch run segment', { runId: existing.runId, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) return null }) @@ -205,14 +205,12 @@ export const POST = withRouteHandler((req: NextRequest) => { logger.error(`[${tracker.requestId}] Unexpected error:`, { duration, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) span.setAttribute(TraceAttr.CopilotConfirmOutcome, CopilotConfirmOutcome.InternalError) - return createInternalServerErrorResponse( - error instanceof Error ? error.message : 'Internal server error' - ) + return createInternalServerErrorResponse(getErrorMessage(error, 'Internal server error')) } } ) diff --git a/apps/sim/app/api/copilot/credentials/route.ts b/apps/sim/app/api/copilot/credentials/route.ts index be665c4938..6d59898422 100644 --- a/apps/sim/app/api/copilot/credentials/route.ts +++ b/apps/sim/app/api/copilot/credentials/route.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { copilotCredentialsContract } from '@/lib/api/contracts/copilot' import { parseRequest } from '@/lib/api/server' @@ -26,7 +27,7 @@ export const GET = withRouteHandler(async (req: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to load credentials', + error: getErrorMessage(error, 'Failed to load credentials'), }, { status: 500 } ) diff --git a/apps/sim/app/api/copilot/feedback/route.ts b/apps/sim/app/api/copilot/feedback/route.ts index 85651ac89b..e14cff30b4 100644 --- a/apps/sim/app/api/copilot/feedback/route.ts +++ b/apps/sim/app/api/copilot/feedback/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { copilotFeedback } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { submitCopilotFeedbackContract } from '@/lib/api/contracts' @@ -104,7 +105,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => { logger.error(`[${tracker.requestId}] Error submitting copilot feedback:`, { duration, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) diff --git a/apps/sim/app/api/copilot/training/examples/route.ts b/apps/sim/app/api/copilot/training/examples/route.ts index 3497428062..e69cfc5ecd 100644 --- a/apps/sim/app/api/copilot/training/examples/route.ts +++ b/apps/sim/app/api/copilot/training/examples/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { copilotTrainingExampleContract } from '@/lib/api/contracts/copilot' import { parseRequest, validationErrorResponse } from '@/lib/api/server' @@ -74,7 +75,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { headers: { 'content-type': 'application/json' }, }) } catch (err) { - const errorMessage = err instanceof Error ? err.message : 'Failed to add example' + const errorMessage = getErrorMessage(err, 'Failed to add example') logger.error('Failed to send workflow example', { error: err }) return NextResponse.json({ error: errorMessage }, { status: 502 }) } diff --git a/apps/sim/app/api/copilot/training/route.ts b/apps/sim/app/api/copilot/training/route.ts index 993961bb29..100b53b77a 100644 --- a/apps/sim/app/api/copilot/training/route.ts +++ b/apps/sim/app/api/copilot/training/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { copilotTrainingDataContract } from '@/lib/api/contracts/copilot' import { parseRequest, validationErrorResponse } from '@/lib/api/server' @@ -85,7 +86,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { logger.error('Failed to send training data to agent indexer', { error }) return NextResponse.json( { - error: error instanceof Error ? error.message : 'Failed to send training data', + error: getErrorMessage(error, 'Failed to send training data'), }, { status: 502 } ) diff --git a/apps/sim/app/api/credential-sets/memberships/route.ts b/apps/sim/app/api/credential-sets/memberships/route.ts index c268e297f4..ee3a5ec3e4 100644 --- a/apps/sim/app/api/credential-sets/memberships/route.ts +++ b/apps/sim/app/api/credential-sets/memberships/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { credentialSet, credentialSetMember, organization } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -132,7 +133,7 @@ export const DELETE = withRouteHandler(async (req: NextRequest) => { return NextResponse.json({ success: true }) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to leave credential set' + const message = getErrorMessage(error, 'Failed to leave credential set') logger.error('Error leaving credential set', error) return NextResponse.json({ error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/files/multipart/route.ts b/apps/sim/app/api/files/multipart/route.ts index bf4d9fd827..8612f0b1f8 100644 --- a/apps/sim/app/api/files/multipart/route.ts +++ b/apps/sim/app/api/files/multipart/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { abortMultipartUploadContract, @@ -438,7 +439,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error('Multipart upload error:', error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Multipart upload failed' }, + { error: getErrorMessage(error, 'Multipart upload failed') }, { status: 500 } ) } diff --git a/apps/sim/app/api/files/parse/route.ts b/apps/sim/app/api/files/parse/route.ts index 4bcd7d0191..aa3c3ff93c 100644 --- a/apps/sim/app/api/files/parse/route.ts +++ b/apps/sim/app/api/files/parse/route.ts @@ -3,6 +3,8 @@ import { createHash } from 'crypto' import fsPromises, { readFile } from 'fs/promises' import path from 'path' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateShortId } from '@sim/utils/id' import binaryExtensionsList from 'binary-extensions' import { type NextRequest, NextResponse } from 'next/server' import { fileParseContract } from '@/lib/api/contracts/storage-transfer' @@ -218,7 +220,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), filePath: '', }, { status: 500 } @@ -552,7 +554,7 @@ async function handleCloudFile( // If file is already from execution context, create UserFile reference without re-uploading if (context === 'execution') { userFile = { - id: `file_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, + id: `file_${Date.now()}_${generateShortId(7)}`, name: filename, url: normalizedFilePath, size: fileBuffer.length, diff --git a/apps/sim/app/api/files/presigned/route.ts b/apps/sim/app/api/files/presigned/route.ts index 8c3eda979d..7484712978 100644 --- a/apps/sim/app/api/files/presigned/route.ts +++ b/apps/sim/app/api/files/presigned/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { presignedUploadBodyContract, uploadTypeSchema } from '@/lib/api/contracts/storage-transfer' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -124,9 +125,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { expirationSeconds: 3600, }) } catch (error) { - throw new ValidationError( - error instanceof Error ? error.message : 'Copilot validation failed' - ) + throw new ValidationError(getErrorMessage(error, 'Copilot validation failed')) } } else if (uploadType === 'mothership') { const workspaceId = request.nextUrl.searchParams.get('workspaceId') diff --git a/apps/sim/app/api/files/upload/route.ts b/apps/sim/app/api/files/upload/route.ts index 828cf83aa0..2bdd3c81d1 100644 --- a/apps/sim/app/api/files/upload/route.ts +++ b/apps/sim/app/api/files/upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { sanitizeFileName } from '@/executor/constants' import '@/lib/uploads/core/setup.server' @@ -218,8 +219,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { uploadResults.push(userFile) continue } catch (workspaceError) { - const errorMessage = - workspaceError instanceof Error ? workspaceError.message : 'Upload failed' + const errorMessage = getErrorMessage(workspaceError, 'Upload failed') const isDuplicate = errorMessage.includes('already exists') const isStorageLimitError = errorMessage.includes('Storage limit exceeded') || diff --git a/apps/sim/app/api/folders/[id]/restore/route.ts b/apps/sim/app/api/folders/[id]/restore/route.ts index 8f74418c53..5ad28b90b2 100644 --- a/apps/sim/app/api/folders/[id]/restore/route.ts +++ b/apps/sim/app/api/folders/[id]/restore/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { restoreFolderContract } from '@/lib/api/contracts' import { parseRequest } from '@/lib/api/server' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest, context: Route } catch (error) { logger.error('Error restoring folder', error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Internal server error' }, + { error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/form/manage/[id]/route.ts b/apps/sim/app/api/form/manage/[id]/route.ts index d1c05bbe62..982a8b36bd 100644 --- a/apps/sim/app/api/form/manage/[id]/route.ts +++ b/apps/sim/app/api/form/manage/[id]/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { form } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { formIdParamsSchema, updateFormContract } from '@/lib/api/contracts/forms' @@ -14,10 +15,6 @@ import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/ const logger = createLogger('FormManageAPI') -function getErrorMessage(error: unknown, fallback: string): string { - return error instanceof Error ? error.message : fallback -} - export const GET = withRouteHandler( async (request: NextRequest, { params }: { params: Promise<{ id: string }> }) => { try { diff --git a/apps/sim/app/api/form/route.ts b/apps/sim/app/api/form/route.ts index c0592bc366..2b232c9132 100644 --- a/apps/sim/app/api/form/route.ts +++ b/apps/sim/app/api/form/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { form } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' @@ -22,10 +23,6 @@ import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/ const logger = createLogger('FormAPI') export const maxDuration = 120 -function getErrorMessage(error: unknown, fallback: string): string { - return error instanceof Error ? error.message : fallback -} - async function cleanupFormAfterDeployFailure(formId: string) { try { await db.delete(form).where(eq(form.id, formId)) diff --git a/apps/sim/app/api/form/validate/route.ts b/apps/sim/app/api/form/validate/route.ts index 42baba4c85..0db7f37ee9 100644 --- a/apps/sim/app/api/form/validate/route.ts +++ b/apps/sim/app/api/form/validate/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { form } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { formIdentifierValidationQuerySchema } from '@/lib/api/contracts/forms' @@ -58,7 +59,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { error: isAvailable ? null : 'This identifier is already in use', }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to validate identifier' + const message = getErrorMessage(error, 'Failed to validate identifier') logger.error('Error validating form identifier:', error) return createErrorResponse(message, 500) } diff --git a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts index 5d0d7ba50d..c977a8c5ed 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { type NextRequest, NextResponse } from 'next/server' import { @@ -212,7 +213,7 @@ export const POST = withRouteHandler( ) } catch (error) { logger.warn(`[${requestId}] Failed to calculate cost for chunk upload`, { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) // Continue without cost information rather than failing the upload } diff --git a/apps/sim/app/api/knowledge/[id]/documents/route.ts b/apps/sim/app/api/knowledge/[id]/documents/route.ts index 822094f32a..128f70ae37 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/route.ts @@ -1,5 +1,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { type NextRequest, NextResponse } from 'next/server' @@ -301,7 +302,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error(`[${requestId}] Error creating document`, error) - const errorMessage = error instanceof Error ? error.message : 'Failed to create document' + const errorMessage = getErrorMessage(error, 'Failed to create document') const isStorageLimitError = errorMessage.includes('Storage limit exceeded') || errorMessage.includes('storage limit') const isMissingKnowledgeBase = errorMessage === 'Knowledge base not found' diff --git a/apps/sim/app/api/knowledge/[id]/documents/upsert/route.ts b/apps/sim/app/api/knowledge/[id]/documents/upsert/route.ts index f167d06d0b..4bde63da80 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/upsert/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/upsert/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { document } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { and, eq, isNull } from 'drizzle-orm' @@ -218,7 +219,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error(`[${requestId}] Error upserting document`, error) - const errorMessage = error instanceof Error ? error.message : 'Failed to upsert document' + const errorMessage = getErrorMessage(error, 'Failed to upsert document') const isStorageLimitError = errorMessage.includes('Storage limit exceeded') || errorMessage.includes('storage limit') const isMissingKnowledgeBase = errorMessage === 'Knowledge base not found' diff --git a/apps/sim/app/api/knowledge/[id]/restore/route.ts b/apps/sim/app/api/knowledge/[id]/restore/route.ts index 92929d23f1..5dee08582a 100644 --- a/apps/sim/app/api/knowledge/[id]/restore/route.ts +++ b/apps/sim/app/api/knowledge/[id]/restore/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { restoreKnowledgeBaseContract } from '@/lib/api/contracts/knowledge' import { parseRequest } from '@/lib/api/server' @@ -58,7 +59,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error(`[${requestId}] Error restoring knowledge base ${id}`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Internal server error' }, + { error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/knowledge/search/route.ts b/apps/sim/app/api/knowledge/search/route.ts index f93c0f5afe..482ee61950 100644 --- a/apps/sim/app/api/knowledge/search/route.ts +++ b/apps/sim/app/api/knowledge/search/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { type NextRequest, NextResponse } from 'next/server' import { knowledgeSearchBodySchema } from '@/lib/api/contracts/knowledge' @@ -339,7 +340,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } } catch (error) { logger.warn(`[${requestId}] Reranker failed; falling back to vector ordering`, { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), model: rerankerModel, candidateCount, workspaceId, @@ -361,7 +362,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { cost = calculateCost(queryEmbeddingModel, tokenCount.count, 0, false) } catch (error) { logger.warn(`[${requestId}] Failed to calculate cost for search query`, { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } } @@ -491,7 +492,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { error: 'Failed to perform vector search', - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/mothership/execute/route.ts b/apps/sim/app/api/mothership/execute/route.ts index 1e1c0ee0bc..c1ca7c03eb 100644 --- a/apps/sim/app/api/mothership/execute/route.ts +++ b/apps/sim/app/api/mothership/execute/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mothershipExecuteContract } from '@/lib/api/contracts/mothership-tasks' @@ -207,12 +207,12 @@ export const POST = withRouteHandler(async (req: NextRequest) => { messageId ? `Mothership execute error [messageId:${messageId}]` : 'Mothership execute error', { requestId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), } ) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Internal server error' }, + { error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/notifications/poll/route.ts b/apps/sim/app/api/notifications/poll/route.ts index 136c41e836..f9e3c8c0c2 100644 --- a/apps/sim/app/api/notifications/poll/route.ts +++ b/apps/sim/app/api/notifications/poll/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { noInputSchema } from '@/lib/api/contracts/primitives' @@ -60,7 +61,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { { success: false, message: 'Inactivity alert polling failed', - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), requestId, }, { status: 500 } diff --git a/apps/sim/app/api/organizations/route.ts b/apps/sim/app/api/organizations/route.ts index 0f170ae0fc..aa87abbcb7 100644 --- a/apps/sim/app/api/organizations/route.ts +++ b/apps/sim/app/api/organizations/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { member, organization, subscription as subscriptionTable } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, inArray, or } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' @@ -66,7 +67,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error('Failed to fetch organizations', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) @@ -230,7 +231,7 @@ export const POST = withRouteHandler(async (request: Request) => { logger.error('Failed to activate organization after creation', { organizationId, userId: user.id, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) } @@ -287,14 +288,14 @@ export const POST = withRouteHandler(async (request: Request) => { } logger.error('Failed to create organization for team plan', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) return NextResponse.json( { error: 'Failed to create organization', - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/providers/fireworks/models/route.ts b/apps/sim/app/api/providers/fireworks/models/route.ts index bb6d036ab0..ef3c000d96 100644 --- a/apps/sim/app/api/providers/fireworks/models/route.ts +++ b/apps/sim/app/api/providers/fireworks/models/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { fireworksProviderModelsQuerySchema, @@ -99,7 +100,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(providerModelsResponseSchema.parse({ models })) } catch (error) { logger.error('Error fetching Fireworks models', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) return NextResponse.json({ models: [] }) } diff --git a/apps/sim/app/api/providers/ollama/models/route.ts b/apps/sim/app/api/providers/ollama/models/route.ts index 1c36632231..47c302ba28 100644 --- a/apps/sim/app/api/providers/ollama/models/route.ts +++ b/apps/sim/app/api/providers/ollama/models/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { ollamaUpstreamResponseSchema, @@ -53,7 +54,7 @@ export const GET = withRouteHandler(async (_request: NextRequest) => { return NextResponse.json(providerModelsResponseSchema.parse({ models })) } catch (error) { logger.error('Failed to fetch Ollama models', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), host: OLLAMA_HOST, }) diff --git a/apps/sim/app/api/providers/openrouter/models/route.ts b/apps/sim/app/api/providers/openrouter/models/route.ts index c44162b7e2..11e53803b7 100644 --- a/apps/sim/app/api/providers/openrouter/models/route.ts +++ b/apps/sim/app/api/providers/openrouter/models/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { openRouterUpstreamResponseSchema, @@ -94,7 +95,7 @@ export const GET = withRouteHandler(async (_request: NextRequest) => { return NextResponse.json(providerModelsResponseSchema.parse({ models, modelInfo })) } catch (error) { logger.error('Error fetching OpenRouter models', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) return NextResponse.json({ models: [], modelInfo: {} }) } diff --git a/apps/sim/app/api/providers/route.ts b/apps/sim/app/api/providers/route.ts index ec5cfcba94..f0bfc2b4a4 100644 --- a/apps/sim/app/api/providers/route.ts +++ b/apps/sim/app/api/providers/route.ts @@ -1,7 +1,7 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { executeProviderContract } from '@/lib/api/contracts/providers' @@ -167,7 +167,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { hasVertexCredential: !!vertexCredential, }) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Credential error' }, + { error: getErrorMessage(error, 'Credential error') }, { status: 400 } ) } diff --git a/apps/sim/app/api/providers/vllm/models/route.ts b/apps/sim/app/api/providers/vllm/models/route.ts index b933e1a981..6e2e167220 100644 --- a/apps/sim/app/api/providers/vllm/models/route.ts +++ b/apps/sim/app/api/providers/vllm/models/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { providerModelsResponseSchema, @@ -65,7 +66,7 @@ export const GET = withRouteHandler(async (_request: NextRequest) => { return NextResponse.json(providerModelsResponseSchema.parse({ models })) } catch (error) { logger.error('Failed to fetch vLLM models', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), baseUrl, }) diff --git a/apps/sim/app/api/skills/import/route.ts b/apps/sim/app/api/skills/import/route.ts index 42912ea8ef..571f476407 100644 --- a/apps/sim/app/api/skills/import/route.ts +++ b/apps/sim/app/api/skills/import/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { importSkillContract } from '@/lib/api/contracts' import { parseRequest } from '@/lib/api/server' @@ -58,7 +59,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => { try { rawUrl = toRawGitHubUrl(url) } catch (err) { - const message = err instanceof Error ? err.message : 'Invalid URL' + const message = getErrorMessage(err, 'Invalid URL') return NextResponse.json({ error: message }, { status: 400 }) } diff --git a/apps/sim/app/api/speech/token/route.ts b/apps/sim/app/api/speech/token/route.ts index 7de0054eca..70da8c2887 100644 --- a/apps/sim/app/api/speech/token/route.ts +++ b/apps/sim/app/api/speech/token/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { speechTokenBodySchema } from '@/lib/api/contracts/media/speech' @@ -171,7 +172,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json({ token: data.token }) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to generate speech token' + const message = getErrorMessage(error, 'Failed to generate speech token') logger.error('Speech token error:', error) return NextResponse.json({ error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/table/[tableId]/restore/route.ts b/apps/sim/app/api/table/[tableId]/restore/route.ts index e7dd16ff85..066b03480c 100644 --- a/apps/sim/app/api/table/[tableId]/restore/route.ts +++ b/apps/sim/app/api/table/[tableId]/restore/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { tableIdParamsSchema } from '@/lib/api/contracts/tables' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' @@ -47,7 +48,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error(`[${requestId}] Error restoring table ${tableId}`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Internal server error' }, + { error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/table/[tableId]/route.ts b/apps/sim/app/api/table/[tableId]/route.ts index 64e2600bb9..0e73ecaaeb 100644 --- a/apps/sim/app/api/table/[tableId]/route.ts +++ b/apps/sim/app/api/table/[tableId]/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { getTableQuerySchema, renameTableContract } from '@/lib/api/contracts/tables' import { isZodError, parseRequest, validationErrorResponse } from '@/lib/api/server/validation' @@ -127,7 +128,7 @@ export const PATCH = withRouteHandler( logger.error(`[${requestId}] Error renaming table:`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to rename table' }, + { error: getErrorMessage(error, 'Failed to rename table') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/asana/create-task/route.ts b/apps/sim/app/api/tools/asana/create-task/route.ts index 0a14cf6fc9..df188ea652 100644 --- a/apps/sim/app/api/tools/asana/create-task/route.ts +++ b/apps/sim/app/api/tools/asana/create-task/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { asanaCreateTaskContract } from '@/lib/api/contracts/tools/asana' import { parseRequest } from '@/lib/api/server' @@ -112,7 +112,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/asana/update-task/route.ts b/apps/sim/app/api/tools/asana/update-task/route.ts index d03b0dee3b..44dabf2404 100644 --- a/apps/sim/app/api/tools/asana/update-task/route.ts +++ b/apps/sim/app/api/tools/asana/update-task/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { asanaUpdateTaskContract } from '@/lib/api/contracts/tools/asana' import { parseRequest } from '@/lib/api/server' @@ -116,7 +116,7 @@ export const PUT = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/athena/create-named-query/route.ts b/apps/sim/app/api/tools/athena/create-named-query/route.ts index f56942b8dd..4eb3c56acf 100644 --- a/apps/sim/app/api/tools/athena/create-named-query/route.ts +++ b/apps/sim/app/api/tools/athena/create-named-query/route.ts @@ -1,5 +1,6 @@ import { CreateNamedQueryCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaCreateNamedQueryContract } from '@/lib/api/contracts/tools/aws/athena-create-named-query' import { parseToolRequest } from '@/lib/api/server' @@ -50,8 +51,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to create Athena named query' + const errorMessage = getErrorMessage(error, 'Failed to create Athena named query') logger.error('CreateNamedQuery failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/get-named-query/route.ts b/apps/sim/app/api/tools/athena/get-named-query/route.ts index 7b13c7da3e..abf3b97838 100644 --- a/apps/sim/app/api/tools/athena/get-named-query/route.ts +++ b/apps/sim/app/api/tools/athena/get-named-query/route.ts @@ -1,5 +1,6 @@ import { GetNamedQueryCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaGetNamedQueryContract } from '@/lib/api/contracts/tools/aws/athena-get-named-query' import { parseToolRequest } from '@/lib/api/server' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to get Athena named query' + const errorMessage = getErrorMessage(error, 'Failed to get Athena named query') logger.error('GetNamedQuery failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/get-query-execution/route.ts b/apps/sim/app/api/tools/athena/get-query-execution/route.ts index 053c72d9c0..899bc096ad 100644 --- a/apps/sim/app/api/tools/athena/get-query-execution/route.ts +++ b/apps/sim/app/api/tools/athena/get-query-execution/route.ts @@ -1,5 +1,6 @@ import { GetQueryExecutionCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaGetQueryExecutionContract } from '@/lib/api/contracts/tools/aws/athena-get-query-execution' import { parseToolRequest } from '@/lib/api/server' @@ -62,8 +63,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to get Athena query execution' + const errorMessage = getErrorMessage(error, 'Failed to get Athena query execution') logger.error('GetQueryExecution failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/get-query-results/route.ts b/apps/sim/app/api/tools/athena/get-query-results/route.ts index b7e6138198..2f1289db06 100644 --- a/apps/sim/app/api/tools/athena/get-query-results/route.ts +++ b/apps/sim/app/api/tools/athena/get-query-results/route.ts @@ -1,5 +1,6 @@ import { GetQueryResultsCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaGetQueryResultsContract } from '@/lib/api/contracts/tools/aws/athena-get-query-results' import { parseToolRequest } from '@/lib/api/server' @@ -68,8 +69,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to get Athena query results' + const errorMessage = getErrorMessage(error, 'Failed to get Athena query results') logger.error('GetQueryResults failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/list-named-queries/route.ts b/apps/sim/app/api/tools/athena/list-named-queries/route.ts index 28b0a081d4..4b9c53d1a6 100644 --- a/apps/sim/app/api/tools/athena/list-named-queries/route.ts +++ b/apps/sim/app/api/tools/athena/list-named-queries/route.ts @@ -1,5 +1,6 @@ import { ListNamedQueriesCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaListNamedQueriesContract } from '@/lib/api/contracts/tools/aws/athena-list-named-queries' import { parseToolRequest } from '@/lib/api/server' @@ -45,8 +46,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to list Athena named queries' + const errorMessage = getErrorMessage(error, 'Failed to list Athena named queries') logger.error('ListNamedQueries failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/list-query-executions/route.ts b/apps/sim/app/api/tools/athena/list-query-executions/route.ts index c025cf62f1..51602fdc93 100644 --- a/apps/sim/app/api/tools/athena/list-query-executions/route.ts +++ b/apps/sim/app/api/tools/athena/list-query-executions/route.ts @@ -1,5 +1,6 @@ import { ListQueryExecutionsCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaListQueryExecutionsContract } from '@/lib/api/contracts/tools/aws/athena-list-query-executions' import { parseToolRequest } from '@/lib/api/server' @@ -45,8 +46,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to list Athena query executions' + const errorMessage = getErrorMessage(error, 'Failed to list Athena query executions') logger.error('ListQueryExecutions failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/start-query/route.ts b/apps/sim/app/api/tools/athena/start-query/route.ts index df0304909a..4d4687d0a6 100644 --- a/apps/sim/app/api/tools/athena/start-query/route.ts +++ b/apps/sim/app/api/tools/athena/start-query/route.ts @@ -1,5 +1,6 @@ import { StartQueryExecutionCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaStartQueryContract } from '@/lib/api/contracts/tools/aws/athena-start-query' import { parseToolRequest } from '@/lib/api/server' @@ -62,7 +63,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to start Athena query' + const errorMessage = getErrorMessage(error, 'Failed to start Athena query') logger.error('StartQuery failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/athena/stop-query/route.ts b/apps/sim/app/api/tools/athena/stop-query/route.ts index 576cd0066b..186c8df8b9 100644 --- a/apps/sim/app/api/tools/athena/stop-query/route.ts +++ b/apps/sim/app/api/tools/athena/stop-query/route.ts @@ -1,5 +1,6 @@ import { StopQueryExecutionCommand } from '@aws-sdk/client-athena' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsAthenaStopQueryContract } from '@/lib/api/contracts/tools/aws/athena-stop-query' import { parseToolRequest } from '@/lib/api/server' @@ -42,7 +43,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to stop Athena query' + const errorMessage = getErrorMessage(error, 'Failed to stop Athena query') logger.error('StopQuery failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/box/upload/route.ts b/apps/sim/app/api/tools/box/upload/route.ts index 73519befcd..95ca997905 100644 --- a/apps/sim/app/api/tools/box/upload/route.ts +++ b/apps/sim/app/api/tools/box/upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { boxUploadContract } from '@/lib/api/contracts/storage-transfer' import { parseRequest } from '@/lib/api/server' @@ -122,7 +123,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Unexpected error:`, error) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts index efd7c7a7b7..a46a22deb5 100644 --- a/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts +++ b/apps/sim/app/api/tools/cloudformation/describe-stack-drift-detection-status/route.ts @@ -3,6 +3,7 @@ import { DescribeStackDriftDetectionStatusCommand, } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationDescribeStackDriftDetectionStatusContract } from '@/lib/api/contracts/tools/aws/cloudformation-describe-stack-drift-detection-status' import { parseToolRequest } from '@/lib/api/server' @@ -56,8 +57,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to describe stack drift detection status' + const errorMessage = getErrorMessage(error, 'Failed to describe stack drift detection status') logger.error('DescribeStackDriftDetectionStatus failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts index 0a772aa10c..64775f3712 100644 --- a/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts +++ b/apps/sim/app/api/tools/cloudformation/describe-stack-events/route.ts @@ -4,6 +4,7 @@ import { type StackEvent, } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationDescribeStackEventsContract } from '@/lib/api/contracts/tools/aws/cloudformation-describe-stack-events' import { parseToolRequest } from '@/lib/api/server' @@ -65,8 +66,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { events }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to describe CloudFormation stack events' + const errorMessage = getErrorMessage(error, 'Failed to describe CloudFormation stack events') logger.error('DescribeStackEvents failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts b/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts index 9949df87cb..b2ff65c972 100644 --- a/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts +++ b/apps/sim/app/api/tools/cloudformation/describe-stacks/route.ts @@ -4,6 +4,7 @@ import { type Stack, } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationDescribeStacksContract } from '@/lib/api/contracts/tools/aws/cloudformation-describe-stacks' import { parseToolRequest } from '@/lib/api/server' @@ -77,8 +78,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { stacks }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to describe CloudFormation stacks' + const errorMessage = getErrorMessage(error, 'Failed to describe CloudFormation stacks') logger.error('DescribeStacks failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts b/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts index 926c5c12fa..d0c8a71957 100644 --- a/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts +++ b/apps/sim/app/api/tools/cloudformation/detect-stack-drift/route.ts @@ -1,5 +1,6 @@ import { CloudFormationClient, DetectStackDriftCommand } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationDetectStackDriftContract } from '@/lib/api/contracts/tools/aws/cloudformation-detect-stack-drift' import { parseToolRequest } from '@/lib/api/server' @@ -47,8 +48,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to detect CloudFormation stack drift' + const errorMessage = getErrorMessage(error, 'Failed to detect CloudFormation stack drift') logger.error('DetectStackDrift failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cloudformation/get-template/route.ts b/apps/sim/app/api/tools/cloudformation/get-template/route.ts index 8429a19c37..3b695de8ca 100644 --- a/apps/sim/app/api/tools/cloudformation/get-template/route.ts +++ b/apps/sim/app/api/tools/cloudformation/get-template/route.ts @@ -1,5 +1,6 @@ import { CloudFormationClient, GetTemplateCommand } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationGetTemplateContract } from '@/lib/api/contracts/tools/aws/cloudformation-get-template' import { parseToolRequest } from '@/lib/api/server' @@ -44,8 +45,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to get CloudFormation template' + const errorMessage = getErrorMessage(error, 'Failed to get CloudFormation template') logger.error('GetTemplate failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts b/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts index 290809ffdf..b10c02872c 100644 --- a/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts +++ b/apps/sim/app/api/tools/cloudformation/list-stack-resources/route.ts @@ -4,6 +4,7 @@ import { type StackResourceSummary, } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationListStackResourcesContract } from '@/lib/api/contracts/tools/aws/cloudformation-list-stack-resources' import { parseToolRequest } from '@/lib/api/server' @@ -66,8 +67,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { resources }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to list CloudFormation stack resources' + const errorMessage = getErrorMessage(error, 'Failed to list CloudFormation stack resources') logger.error('ListStackResources failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cloudformation/validate-template/route.ts b/apps/sim/app/api/tools/cloudformation/validate-template/route.ts index 86bbfa89c3..da1af83142 100644 --- a/apps/sim/app/api/tools/cloudformation/validate-template/route.ts +++ b/apps/sim/app/api/tools/cloudformation/validate-template/route.ts @@ -1,5 +1,6 @@ import { CloudFormationClient, ValidateTemplateCommand } from '@aws-sdk/client-cloudformation' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsCloudformationValidateTemplateContract } from '@/lib/api/contracts/tools/aws/cloudformation-validate-template' import { parseToolRequest } from '@/lib/api/server' @@ -52,8 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to validate CloudFormation template' + const errorMessage = getErrorMessage(error, 'Failed to validate CloudFormation template') logger.error('ValidateTemplate failed', { error: errorMessage }) return NextResponse.json({ error: errorMessage }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts index 32ae35a0a2..be4932a996 100644 --- a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts +++ b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { confluenceUploadAttachmentContract } from '@/lib/api/contracts/selectors/confluence' import { parseRequest } from '@/lib/api/server' @@ -76,7 +77,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { userFile = processSingleFileToUserFile(fileToProcess, 'confluence-upload', logger) } catch (error) { return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to process file' }, + { error: getErrorMessage(error, 'Failed to process file') }, { status: 400 } ) } @@ -96,7 +97,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { logger.error('Failed to download file from storage:', error) return NextResponse.json( { - error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to download file: ${getErrorMessage(error, 'Unknown error')}`, }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/crowdstrike/query/route.ts b/apps/sim/app/api/tools/crowdstrike/query/route.ts index 280775b3b4..92335d15c5 100644 --- a/apps/sim/app/api/tools/crowdstrike/query/route.ts +++ b/apps/sim/app/api/tools/crowdstrike/query/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { crowdstrikeQueryContract } from '@/lib/api/contracts/tools/crowdstrike' @@ -405,7 +406,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: normalizeAggregatesOutput(aggregateData), }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = toError(error).message logger.error(`[${requestId}] CrowdStrike request failed`, { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/cursor/download-artifact/route.ts b/apps/sim/app/api/tools/cursor/download-artifact/route.ts index c94109c592..b32e78ddd1 100644 --- a/apps/sim/app/api/tools/cursor/download-artifact/route.ts +++ b/apps/sim/app/api/tools/cursor/download-artifact/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { cursorDownloadArtifactContract } from '@/lib/api/contracts/tools/cursor' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -151,7 +152,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Error downloading Cursor artifact:`, error) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' }, + { success: false, error: getErrorMessage(error, 'Unknown error occurred') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/custom/route.ts b/apps/sim/app/api/tools/custom/route.ts index 7bf95fbe1f..05f76cef2a 100644 --- a/apps/sim/app/api/tools/custom/route.ts +++ b/apps/sim/app/api/tools/custom/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { customTools } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { and, desc, eq, isNull, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -182,7 +183,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => { return NextResponse.json({ success: true, data: resultTools }) } catch (error) { logger.error(`[${requestId}] Error updating custom tools`, error) - const errorMessage = error instanceof Error ? error.message : 'Failed to update custom tools' + const errorMessage = getErrorMessage(error, 'Failed to update custom tools') return NextResponse.json({ error: errorMessage }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/discord/send-message/route.ts b/apps/sim/app/api/tools/discord/send-message/route.ts index 9a4c24096d..5efc1b44d2 100644 --- a/apps/sim/app/api/tools/discord/send-message/route.ts +++ b/apps/sim/app/api/tools/discord/send-message/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { discordSendMessageContract } from '@/lib/api/contracts/tools/communication/discord' import { parseRequest } from '@/lib/api/server' @@ -150,7 +151,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -208,7 +209,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/docusign/route.ts b/apps/sim/app/api/tools/docusign/route.ts index 04c363def3..c88878bb73 100644 --- a/apps/sim/app/api/tools/docusign/route.ts +++ b/apps/sim/app/api/tools/docusign/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { docusignToolContract } from '@/lib/api/contracts/tools/docusign' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -108,7 +109,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } } catch (error) { logger.error('DocuSign API error', { operation, error }) - const message = error instanceof Error ? error.message : 'Internal server error' + const message = getErrorMessage(error, 'Internal server error') return NextResponse.json({ success: false, error: message }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/dropbox/upload/route.ts b/apps/sim/app/api/tools/dropbox/upload/route.ts index 2c14fcd0dc..d9f375c819 100644 --- a/apps/sim/app/api/tools/dropbox/upload/route.ts +++ b/apps/sim/app/api/tools/dropbox/upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { dropboxUploadContract } from '@/lib/api/contracts/storage-transfer' import { parseRequest } from '@/lib/api/server' @@ -110,7 +111,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Unexpected error:`, error) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/evernote/copy-note/route.ts b/apps/sim/app/api/tools/evernote/copy-note/route.ts index d3ee0699df..cddd0c2d40 100644 --- a/apps/sim/app/api/tools/evernote/copy-note/route.ts +++ b/apps/sim/app/api/tools/evernote/copy-note/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteCopyNoteContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { note }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to copy note', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/create-note/route.ts b/apps/sim/app/api/tools/evernote/create-note/route.ts index 3230109e95..3e8aa3f1e9 100644 --- a/apps/sim/app/api/tools/evernote/create-note/route.ts +++ b/apps/sim/app/api/tools/evernote/create-note/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteCreateNoteContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -54,7 +55,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { note }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to create note', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/create-notebook/route.ts b/apps/sim/app/api/tools/evernote/create-notebook/route.ts index 33fa687ecf..c8a5ddd9c7 100644 --- a/apps/sim/app/api/tools/evernote/create-notebook/route.ts +++ b/apps/sim/app/api/tools/evernote/create-notebook/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteCreateNotebookContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { notebook }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to create notebook', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/create-tag/route.ts b/apps/sim/app/api/tools/evernote/create-tag/route.ts index bdde802b92..d0bd66517c 100644 --- a/apps/sim/app/api/tools/evernote/create-tag/route.ts +++ b/apps/sim/app/api/tools/evernote/create-tag/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteCreateTagContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { tag }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to create tag', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/delete-note/route.ts b/apps/sim/app/api/tools/evernote/delete-note/route.ts index 48c26786a9..c1d4d6f4e9 100644 --- a/apps/sim/app/api/tools/evernote/delete-note/route.ts +++ b/apps/sim/app/api/tools/evernote/delete-note/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteDeleteNoteContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -44,7 +45,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to delete note', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/get-note/route.ts b/apps/sim/app/api/tools/evernote/get-note/route.ts index 55b2c0ace1..5947a28ed9 100644 --- a/apps/sim/app/api/tools/evernote/get-note/route.ts +++ b/apps/sim/app/api/tools/evernote/get-note/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteGetNoteContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { note }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to get note', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/get-notebook/route.ts b/apps/sim/app/api/tools/evernote/get-notebook/route.ts index 464d1e2814..08bb24f26c 100644 --- a/apps/sim/app/api/tools/evernote/get-notebook/route.ts +++ b/apps/sim/app/api/tools/evernote/get-notebook/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteGetNotebookContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { notebook }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to get notebook', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/list-notebooks/route.ts b/apps/sim/app/api/tools/evernote/list-notebooks/route.ts index 3280a83328..bf55d7a87c 100644 --- a/apps/sim/app/api/tools/evernote/list-notebooks/route.ts +++ b/apps/sim/app/api/tools/evernote/list-notebooks/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteListNotebooksContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { notebooks }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to list notebooks', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/list-tags/route.ts b/apps/sim/app/api/tools/evernote/list-tags/route.ts index 5162dcba27..ffbe255c16 100644 --- a/apps/sim/app/api/tools/evernote/list-tags/route.ts +++ b/apps/sim/app/api/tools/evernote/list-tags/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteListTagsContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -41,7 +42,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { tags }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to list tags', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/search-notes/route.ts b/apps/sim/app/api/tools/evernote/search-notes/route.ts index 6b14897a31..ae9a312024 100644 --- a/apps/sim/app/api/tools/evernote/search-notes/route.ts +++ b/apps/sim/app/api/tools/evernote/search-notes/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteSearchNotesContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to search notes', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/evernote/update-note/route.ts b/apps/sim/app/api/tools/evernote/update-note/route.ts index 896a17a817..f1d816328e 100644 --- a/apps/sim/app/api/tools/evernote/update-note/route.ts +++ b/apps/sim/app/api/tools/evernote/update-note/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { evernoteUpdateNoteContract } from '@/lib/api/contracts/tools/evernote' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -61,7 +62,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: { note }, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Failed to update note', { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/extend/parse/route.ts b/apps/sim/app/api/tools/extend/parse/route.ts index 1f0cf8324e..2c5a2e7372 100644 --- a/apps/sim/app/api/tools/extend/parse/route.ts +++ b/apps/sim/app/api/tools/extend/parse/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { extendParseContract } from '@/lib/api/contracts/tools/media/document-parse' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -180,7 +181,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/file/manage/route.ts b/apps/sim/app/api/tools/file/manage/route.ts index cfb53f0643..f95b4a9941 100644 --- a/apps/sim/app/api/tools/file/manage/route.ts +++ b/apps/sim/app/api/tools/file/manage/route.ts @@ -1,4 +1,6 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateShortId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { fileManageContract } from '@/lib/api/contracts/tools/file' import { parseRequest } from '@/lib/api/server' @@ -314,7 +316,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } const lockKey = `file-append:${workspaceId}:${existing.id}` - const lockValue = `${Date.now()}-${Math.random().toString(36).slice(2)}` + const lockValue = `${Date.now()}-${generateShortId()}` const acquired = await acquireLock(lockKey, lockValue, 30) if (!acquired) { return NextResponse.json( @@ -350,7 +352,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } } } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('File operation failed', { operation: body.operation, error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/github/latest-commit/route.ts b/apps/sim/app/api/tools/github/latest-commit/route.ts index 8a5cdd9115..ed9575c997 100644 --- a/apps/sim/app/api/tools/github/latest-commit/route.ts +++ b/apps/sim/app/api/tools/github/latest-commit/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { githubLatestCommitContract } from '@/lib/api/contracts/tools/github' import { parseRequest } from '@/lib/api/server' @@ -182,7 +183,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/add-label/route.ts b/apps/sim/app/api/tools/gmail/add-label/route.ts index c47f4d4d6a..41da810a2f 100644 --- a/apps/sim/app/api/tools/gmail/add-label/route.ts +++ b/apps/sim/app/api/tools/gmail/add-label/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailAddLabelContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -118,7 +119,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/archive/route.ts b/apps/sim/app/api/tools/gmail/archive/route.ts index 26dca5fac5..46161021ff 100644 --- a/apps/sim/app/api/tools/gmail/archive/route.ts +++ b/apps/sim/app/api/tools/gmail/archive/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailArchiveContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -88,7 +89,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/delete/route.ts b/apps/sim/app/api/tools/gmail/delete/route.ts index 79598baad0..ba83d8bf74 100644 --- a/apps/sim/app/api/tools/gmail/delete/route.ts +++ b/apps/sim/app/api/tools/gmail/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailDeleteContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -85,7 +86,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/draft/route.ts b/apps/sim/app/api/tools/gmail/draft/route.ts index 1b51ff59d2..ca4ab7b259 100644 --- a/apps/sim/app/api/tools/gmail/draft/route.ts +++ b/apps/sim/app/api/tools/gmail/draft/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailDraftContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -103,7 +104,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -198,7 +199,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/edit-draft/route.ts b/apps/sim/app/api/tools/gmail/edit-draft/route.ts index dc8b3e7178..cac63c2ab9 100644 --- a/apps/sim/app/api/tools/gmail/edit-draft/route.ts +++ b/apps/sim/app/api/tools/gmail/edit-draft/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailEditDraftContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -99,7 +100,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -191,7 +192,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/mark-read/route.ts b/apps/sim/app/api/tools/gmail/mark-read/route.ts index c7c1700962..a9843788a6 100644 --- a/apps/sim/app/api/tools/gmail/mark-read/route.ts +++ b/apps/sim/app/api/tools/gmail/mark-read/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailMarkReadContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -88,7 +89,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/mark-unread/route.ts b/apps/sim/app/api/tools/gmail/mark-unread/route.ts index 64082a1840..d56f8328c1 100644 --- a/apps/sim/app/api/tools/gmail/mark-unread/route.ts +++ b/apps/sim/app/api/tools/gmail/mark-unread/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailMarkUnreadContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -91,7 +92,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/move/route.ts b/apps/sim/app/api/tools/gmail/move/route.ts index fc088a6d73..fb16b0c363 100644 --- a/apps/sim/app/api/tools/gmail/move/route.ts +++ b/apps/sim/app/api/tools/gmail/move/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailMoveContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -110,7 +111,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/remove-label/route.ts b/apps/sim/app/api/tools/gmail/remove-label/route.ts index f68eeffc98..537f245217 100644 --- a/apps/sim/app/api/tools/gmail/remove-label/route.ts +++ b/apps/sim/app/api/tools/gmail/remove-label/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailRemoveLabelContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -121,7 +122,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/send/route.ts b/apps/sim/app/api/tools/gmail/send/route.ts index d0e6d1b640..81818bfc98 100644 --- a/apps/sim/app/api/tools/gmail/send/route.ts +++ b/apps/sim/app/api/tools/gmail/send/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailSendContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -103,7 +104,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -193,7 +194,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/gmail/unarchive/route.ts b/apps/sim/app/api/tools/gmail/unarchive/route.ts index a0eaf2d1f9..3f81f63389 100644 --- a/apps/sim/app/api/tools/gmail/unarchive/route.ts +++ b/apps/sim/app/api/tools/gmail/unarchive/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { gmailUnarchiveContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -88,7 +89,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/google_drive/download/route.ts b/apps/sim/app/api/tools/google_drive/download/route.ts index 69fc234487..8d063b8058 100644 --- a/apps/sim/app/api/tools/google_drive/download/route.ts +++ b/apps/sim/app/api/tools/google_drive/download/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { googleDriveDownloadContract } from '@/lib/api/contracts/tools/google' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -271,7 +272,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/google_drive/upload/route.ts b/apps/sim/app/api/tools/google_drive/upload/route.ts index 797d7cf11d..9c0cd1ccd9 100644 --- a/apps/sim/app/api/tools/google_drive/upload/route.ts +++ b/apps/sim/app/api/tools/google_drive/upload/route.ts @@ -1,4 +1,6 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateShortId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { googleDriveUploadContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -102,7 +104,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -126,7 +128,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to download file: ${getErrorMessage(error, 'Unknown error')}`, }, { status: 500 } ) @@ -170,7 +172,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { metadata.parents = [validatedData.folderId.trim()] } - const boundary = `boundary_${Date.now()}_${Math.random().toString(36).substring(7)}` + const boundary = `boundary_${Date.now()}_${generateShortId(7)}` const multipartBody = buildMultipartBody(metadata, fileBuffer, uploadMimeType, boundary) @@ -278,7 +280,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/google_vault/download-export-file/route.ts b/apps/sim/app/api/tools/google_vault/download-export-file/route.ts index fec31be006..9a492698af 100644 --- a/apps/sim/app/api/tools/google_vault/download-export-file/route.ts +++ b/apps/sim/app/api/tools/google_vault/download-export-file/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { googleVaultDownloadExportFileContract } from '@/lib/api/contracts/tools/google' import { parseRequest } from '@/lib/api/server' @@ -119,7 +120,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/imap/mailboxes/route.ts b/apps/sim/app/api/tools/imap/mailboxes/route.ts index 7230d440e2..b66c9eb34d 100644 --- a/apps/sim/app/api/tools/imap/mailboxes/route.ts +++ b/apps/sim/app/api/tools/imap/mailboxes/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { ImapFlow } from 'imapflow' import { type NextRequest, NextResponse } from 'next/server' import { imapMailboxesContract } from '@/lib/api/contracts/tools/imap' @@ -93,7 +94,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { throw error } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error('Error fetching IMAP mailboxes:', errorMessage) let userMessage = 'Failed to connect to IMAP server. Please check your connection settings.' diff --git a/apps/sim/app/api/tools/jira/add-attachment/route.ts b/apps/sim/app/api/tools/jira/add-attachment/route.ts index 6f343879d3..2fc2e89f2a 100644 --- a/apps/sim/app/api/tools/jira/add-attachment/route.ts +++ b/apps/sim/app/api/tools/jira/add-attachment/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jiraAddAttachmentContract } from '@/lib/api/contracts/selectors/jira' import { parseRequest } from '@/lib/api/server' @@ -105,7 +106,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Jira attachment upload error`, error) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Internal server error' }, + { success: false, error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/jira/update/route.ts b/apps/sim/app/api/tools/jira/update/route.ts index 75327d15d6..f8357b18bd 100644 --- a/apps/sim/app/api/tools/jira/update/route.ts +++ b/apps/sim/app/api/tools/jira/update/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jiraUpdateContract } from '@/lib/api/contracts/selectors/jira' import { parseRequest } from '@/lib/api/server' @@ -167,7 +167,7 @@ export const PUT = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jira/write/route.ts b/apps/sim/app/api/tools/jira/write/route.ts index 1e8899eb2a..916ee57c5a 100644 --- a/apps/sim/app/api/tools/jira/write/route.ts +++ b/apps/sim/app/api/tools/jira/write/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jiraWriteContract } from '@/lib/api/contracts/selectors/jira' import { parseRequest } from '@/lib/api/server' @@ -242,7 +242,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/approvals/route.ts b/apps/sim/app/api/tools/jsm/approvals/route.ts index 76a65b7a4c..ba8f269204 100644 --- a/apps/sim/app/api/tools/jsm/approvals/route.ts +++ b/apps/sim/app/api/tools/jsm/approvals/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmApprovalsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -211,7 +211,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/comment/route.ts b/apps/sim/app/api/tools/jsm/comment/route.ts index 3f464ad1ce..fb48eef55c 100644 --- a/apps/sim/app/api/tools/jsm/comment/route.ts +++ b/apps/sim/app/api/tools/jsm/comment/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmCommentContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -125,7 +125,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/comments/route.ts b/apps/sim/app/api/tools/jsm/comments/route.ts index a918bbe89d..8741ff4366 100644 --- a/apps/sim/app/api/tools/jsm/comments/route.ts +++ b/apps/sim/app/api/tools/jsm/comments/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmCommentsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -117,7 +117,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/customers/route.ts b/apps/sim/app/api/tools/jsm/customers/route.ts index bd244d9dcc..4b52715ea0 100644 --- a/apps/sim/app/api/tools/jsm/customers/route.ts +++ b/apps/sim/app/api/tools/jsm/customers/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmCustomersContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -177,7 +177,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/answers/route.ts b/apps/sim/app/api/tools/jsm/forms/answers/route.ts index aa204cc6f0..7e43530852 100644 --- a/apps/sim/app/api/tools/jsm/forms/answers/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/answers/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmFormAnswersContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -108,7 +108,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/attach/route.ts b/apps/sim/app/api/tools/jsm/forms/attach/route.ts index a7ee4cc9d1..0a25aee374 100644 --- a/apps/sim/app/api/tools/jsm/forms/attach/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/attach/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmAttachFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -122,7 +122,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/copy/route.ts b/apps/sim/app/api/tools/jsm/forms/copy/route.ts index bf27d1d5c4..dc32fec761 100644 --- a/apps/sim/app/api/tools/jsm/forms/copy/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/copy/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmCopyFormsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -123,7 +123,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/delete/route.ts b/apps/sim/app/api/tools/jsm/forms/delete/route.ts index d0405ab957..67c8f5fc66 100644 --- a/apps/sim/app/api/tools/jsm/forms/delete/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/delete/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmDeleteFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -108,7 +108,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/externalise/route.ts b/apps/sim/app/api/tools/jsm/forms/externalise/route.ts index 7e812406a8..a41823e211 100644 --- a/apps/sim/app/api/tools/jsm/forms/externalise/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/externalise/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmExternaliseFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -109,7 +109,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/get/route.ts b/apps/sim/app/api/tools/jsm/forms/get/route.ts index f885a9cbd4..278cd6b1ca 100644 --- a/apps/sim/app/api/tools/jsm/forms/get/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/get/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmGetFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -110,7 +110,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/internalise/route.ts b/apps/sim/app/api/tools/jsm/forms/internalise/route.ts index 6f695fab0e..d38975aa89 100644 --- a/apps/sim/app/api/tools/jsm/forms/internalise/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/internalise/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmInternaliseFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -109,7 +109,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/issue/route.ts b/apps/sim/app/api/tools/jsm/forms/issue/route.ts index b47028d170..2f944aabc3 100644 --- a/apps/sim/app/api/tools/jsm/forms/issue/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/issue/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmIssueFormsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -108,7 +108,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/reopen/route.ts b/apps/sim/app/api/tools/jsm/forms/reopen/route.ts index 98335b431e..718a1ff728 100644 --- a/apps/sim/app/api/tools/jsm/forms/reopen/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/reopen/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmReopenFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -109,7 +109,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/save/route.ts b/apps/sim/app/api/tools/jsm/forms/save/route.ts index b8bd7b54d3..76d1000da3 100644 --- a/apps/sim/app/api/tools/jsm/forms/save/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/save/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmSaveFormAnswersContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -122,7 +122,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/structure/route.ts b/apps/sim/app/api/tools/jsm/forms/structure/route.ts index c67ddb5f99..b82b1f1ea4 100644 --- a/apps/sim/app/api/tools/jsm/forms/structure/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/structure/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmProjectFormStructureContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -110,7 +110,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/submit/route.ts b/apps/sim/app/api/tools/jsm/forms/submit/route.ts index c7d8656c1d..a23e98c7d3 100644 --- a/apps/sim/app/api/tools/jsm/forms/submit/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/submit/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmSubmitFormContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -109,7 +109,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/forms/templates/route.ts b/apps/sim/app/api/tools/jsm/forms/templates/route.ts index 1f0c66b397..681ff6903c 100644 --- a/apps/sim/app/api/tools/jsm/forms/templates/route.ts +++ b/apps/sim/app/api/tools/jsm/forms/templates/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmProjectFormTemplatesContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -108,7 +108,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/organization/route.ts b/apps/sim/app/api/tools/jsm/organization/route.ts index 5c69d8664b..3e0fb3ccd5 100644 --- a/apps/sim/app/api/tools/jsm/organization/route.ts +++ b/apps/sim/app/api/tools/jsm/organization/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmOrganizationContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -189,7 +189,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/organizations/route.ts b/apps/sim/app/api/tools/jsm/organizations/route.ts index f06712a949..769dfec7eb 100644 --- a/apps/sim/app/api/tools/jsm/organizations/route.ts +++ b/apps/sim/app/api/tools/jsm/organizations/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmOrganizationsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -110,7 +110,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/participants/route.ts b/apps/sim/app/api/tools/jsm/participants/route.ts index 581b191968..496c587c81 100644 --- a/apps/sim/app/api/tools/jsm/participants/route.ts +++ b/apps/sim/app/api/tools/jsm/participants/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmParticipantsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -186,7 +186,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/queues/route.ts b/apps/sim/app/api/tools/jsm/queues/route.ts index 790e550d80..d966c682f0 100644 --- a/apps/sim/app/api/tools/jsm/queues/route.ts +++ b/apps/sim/app/api/tools/jsm/queues/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmQueuesContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -112,7 +112,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/request/route.ts b/apps/sim/app/api/tools/jsm/request/route.ts index e833068187..3b2e9922df 100644 --- a/apps/sim/app/api/tools/jsm/request/route.ts +++ b/apps/sim/app/api/tools/jsm/request/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmRequestContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -262,7 +262,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/requests/route.ts b/apps/sim/app/api/tools/jsm/requests/route.ts index a73fb59646..449d0d5225 100644 --- a/apps/sim/app/api/tools/jsm/requests/route.ts +++ b/apps/sim/app/api/tools/jsm/requests/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmRequestsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -152,7 +152,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/requesttypefields/route.ts b/apps/sim/app/api/tools/jsm/requesttypefields/route.ts index ffab68c310..cf6d043943 100644 --- a/apps/sim/app/api/tools/jsm/requesttypefields/route.ts +++ b/apps/sim/app/api/tools/jsm/requesttypefields/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmRequestTypeFieldsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -126,7 +126,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/requesttypes/route.ts b/apps/sim/app/api/tools/jsm/requesttypes/route.ts index 56989b0049..e16a39022e 100644 --- a/apps/sim/app/api/tools/jsm/requesttypes/route.ts +++ b/apps/sim/app/api/tools/jsm/requesttypes/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmRequestTypesContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -116,7 +116,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/servicedesks/route.ts b/apps/sim/app/api/tools/jsm/servicedesks/route.ts index b7a8d6d8b4..3ca85dd916 100644 --- a/apps/sim/app/api/tools/jsm/servicedesks/route.ts +++ b/apps/sim/app/api/tools/jsm/servicedesks/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmServiceDesksContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -94,7 +94,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/sla/route.ts b/apps/sim/app/api/tools/jsm/sla/route.ts index 1ebf5613ca..64bd97a035 100644 --- a/apps/sim/app/api/tools/jsm/sla/route.ts +++ b/apps/sim/app/api/tools/jsm/sla/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmSlaContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -111,7 +111,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/transition/route.ts b/apps/sim/app/api/tools/jsm/transition/route.ts index 8b1027d8c5..62b2adfa96 100644 --- a/apps/sim/app/api/tools/jsm/transition/route.ts +++ b/apps/sim/app/api/tools/jsm/transition/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmTransitionContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -129,7 +129,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/jsm/transitions/route.ts b/apps/sim/app/api/tools/jsm/transitions/route.ts index 8d3199b9dd..364c999b08 100644 --- a/apps/sim/app/api/tools/jsm/transitions/route.ts +++ b/apps/sim/app/api/tools/jsm/transitions/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { jsmTransitionsContract } from '@/lib/api/contracts/selectors/jsm' import { parseRequest } from '@/lib/api/server' @@ -111,7 +111,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), success: false, }, { status: 500 } diff --git a/apps/sim/app/api/tools/microsoft-dataverse/upload-file/route.ts b/apps/sim/app/api/tools/microsoft-dataverse/upload-file/route.ts index 2bfca548e7..dbf5b10093 100644 --- a/apps/sim/app/api/tools/microsoft-dataverse/upload-file/route.ts +++ b/apps/sim/app/api/tools/microsoft-dataverse/upload-file/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { dataverseUploadFileContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -61,7 +62,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -132,7 +133,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { logger.error(`[${requestId}] Error uploading file to Dataverse:`, error) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Internal server error' }, + { success: false, error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/microsoft_excel/sheets/route.ts b/apps/sim/app/api/tools/microsoft_excel/sheets/route.ts index 19212aaa59..f08f968734 100644 --- a/apps/sim/app/api/tools/microsoft_excel/sheets/route.ts +++ b/apps/sim/app/api/tools/microsoft_excel/sheets/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { microsoftExcelSheetsSelectorContract } from '@/lib/api/contracts/selectors/microsoft' import { parseRequest } from '@/lib/api/server' @@ -59,7 +60,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { basePath = getItemBasePath(spreadsheetId, driveId) } catch (error) { return NextResponse.json( - { error: error instanceof Error ? error.message : 'Invalid parameters' }, + { error: getErrorMessage(error, 'Invalid parameters') }, { status: 400 } ) } diff --git a/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts b/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts index 0604678732..113cd53331 100644 --- a/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { teamsDeleteChatMessageContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -110,7 +111,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts b/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts index 208261b9b8..58fb3cfd94 100644 --- a/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { teamsWriteChannelContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -170,7 +171,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts b/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts index ce350752da..96a4dada98 100644 --- a/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { teamsWriteChatContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -167,7 +168,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/mistral/parse/route.ts b/apps/sim/app/api/tools/mistral/parse/route.ts index c684b9c449..acfd066879 100644 --- a/apps/sim/app/api/tools/mistral/parse/route.ts +++ b/apps/sim/app/api/tools/mistral/parse/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { mistralParseContract } from '@/lib/api/contracts/tools/media/document-parse' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -96,7 +97,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -269,7 +270,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/mongodb/delete/route.ts b/apps/sim/app/api/tools/mongodb/delete/route.ts index 8d071ee30f..423f5f5461 100644 --- a/apps/sim/app/api/tools/mongodb/delete/route.ts +++ b/apps/sim/app/api/tools/mongodb/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mongodbDeleteContract } from '@/lib/api/contracts/tools/databases/mongodb' @@ -78,7 +79,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { deletedCount: result.deletedCount, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MongoDB delete failed:`, error) return NextResponse.json({ error: `MongoDB delete failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mongodb/execute/route.ts b/apps/sim/app/api/tools/mongodb/execute/route.ts index a137065e56..bb8f87abdd 100644 --- a/apps/sim/app/api/tools/mongodb/execute/route.ts +++ b/apps/sim/app/api/tools/mongodb/execute/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mongodbExecuteContract } from '@/lib/api/contracts/tools/databases/mongodb' @@ -71,7 +72,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { documentCount: documents.length, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MongoDB aggregation failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/mongodb/insert/route.ts b/apps/sim/app/api/tools/mongodb/insert/route.ts index 98378dd648..a5cc1c3b21 100644 --- a/apps/sim/app/api/tools/mongodb/insert/route.ts +++ b/apps/sim/app/api/tools/mongodb/insert/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mongodbInsertContract } from '@/lib/api/contracts/tools/databases/mongodb' @@ -61,7 +62,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { documentCount: insertedCount, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MongoDB insert failed:`, error) return NextResponse.json({ error: `MongoDB insert failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mongodb/introspect/route.ts b/apps/sim/app/api/tools/mongodb/introspect/route.ts index f147d979ba..61abc03375 100644 --- a/apps/sim/app/api/tools/mongodb/introspect/route.ts +++ b/apps/sim/app/api/tools/mongodb/introspect/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mongodbIntrospectContract } from '@/lib/api/contracts/tools/databases/mongodb' @@ -50,7 +51,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { collections: result.collections, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MongoDB introspect failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/mongodb/query/route.ts b/apps/sim/app/api/tools/mongodb/query/route.ts index a9a5faada4..0b9cfd4ba4 100644 --- a/apps/sim/app/api/tools/mongodb/query/route.ts +++ b/apps/sim/app/api/tools/mongodb/query/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mongodbQueryContract } from '@/lib/api/contracts/tools/databases/mongodb' @@ -91,7 +92,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { documentCount: documents.length, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MongoDB query failed:`, error) return NextResponse.json({ error: `MongoDB query failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mongodb/update/route.ts b/apps/sim/app/api/tools/mongodb/update/route.ts index 4cc52894e3..1163014207 100644 --- a/apps/sim/app/api/tools/mongodb/update/route.ts +++ b/apps/sim/app/api/tools/mongodb/update/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mongodbUpdateContract } from '@/lib/api/contracts/tools/databases/mongodb' @@ -88,7 +89,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { ...(result.upsertedId && { insertedId: result.upsertedId.toString() }), }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MongoDB update failed:`, error) return NextResponse.json({ error: `MongoDB update failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mysql/delete/route.ts b/apps/sim/app/api/tools/mysql/delete/route.ts index 030bc18105..5fedac77ff 100644 --- a/apps/sim/app/api/tools/mysql/delete/route.ts +++ b/apps/sim/app/api/tools/mysql/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mysqlDeleteContract } from '@/lib/api/contracts/tools/databases/mysql' @@ -51,7 +52,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await connection.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MySQL delete failed:`, error) return NextResponse.json({ error: `MySQL delete failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mysql/execute/route.ts b/apps/sim/app/api/tools/mysql/execute/route.ts index 09d5c1d3b6..cdebfeb810 100644 --- a/apps/sim/app/api/tools/mysql/execute/route.ts +++ b/apps/sim/app/api/tools/mysql/execute/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mysqlExecuteContract } from '@/lib/api/contracts/tools/databases/mysql' @@ -59,7 +60,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await connection.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MySQL execute failed:`, error) return NextResponse.json({ error: `MySQL execute failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mysql/insert/route.ts b/apps/sim/app/api/tools/mysql/insert/route.ts index d43f32df88..5c2e81f278 100644 --- a/apps/sim/app/api/tools/mysql/insert/route.ts +++ b/apps/sim/app/api/tools/mysql/insert/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mysqlInsertContract } from '@/lib/api/contracts/tools/databases/mysql' @@ -51,7 +52,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await connection.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MySQL insert failed:`, error) return NextResponse.json({ error: `MySQL insert failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mysql/introspect/route.ts b/apps/sim/app/api/tools/mysql/introspect/route.ts index c2b18f5297..6f4cad144a 100644 --- a/apps/sim/app/api/tools/mysql/introspect/route.ts +++ b/apps/sim/app/api/tools/mysql/introspect/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mysqlIntrospectContract } from '@/lib/api/contracts/tools/databases/mysql' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await connection.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MySQL introspection failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/mysql/query/route.ts b/apps/sim/app/api/tools/mysql/query/route.ts index b494b2801b..e1ff6c039e 100644 --- a/apps/sim/app/api/tools/mysql/query/route.ts +++ b/apps/sim/app/api/tools/mysql/query/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mysqlQueryContract } from '@/lib/api/contracts/tools/databases/mysql' @@ -59,7 +60,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await connection.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MySQL query failed:`, error) return NextResponse.json({ error: `MySQL query failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/mysql/update/route.ts b/apps/sim/app/api/tools/mysql/update/route.ts index 0f69ac36e7..5237c2a4b5 100644 --- a/apps/sim/app/api/tools/mysql/update/route.ts +++ b/apps/sim/app/api/tools/mysql/update/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { mysqlUpdateContract } from '@/lib/api/contracts/tools/databases/mysql' @@ -51,7 +52,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await connection.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] MySQL update failed:`, error) return NextResponse.json({ error: `MySQL update failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/neo4j/create/route.ts b/apps/sim/app/api/tools/neo4j/create/route.ts index 5dc0225d98..edd3261783 100644 --- a/apps/sim/app/api/tools/neo4j/create/route.ts +++ b/apps/sim/app/api/tools/neo4j/create/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jCreateContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -94,7 +95,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { summary, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j create failed:`, error) return NextResponse.json({ error: `Neo4j create failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/neo4j/delete/route.ts b/apps/sim/app/api/tools/neo4j/delete/route.ts index 35b5584c4f..449d0122e3 100644 --- a/apps/sim/app/api/tools/neo4j/delete/route.ts +++ b/apps/sim/app/api/tools/neo4j/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jDeleteContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -78,7 +79,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { summary, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j delete failed:`, error) return NextResponse.json({ error: `Neo4j delete failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/neo4j/execute/route.ts b/apps/sim/app/api/tools/neo4j/execute/route.ts index 6e5b90db5c..bf36ca56f0 100644 --- a/apps/sim/app/api/tools/neo4j/execute/route.ts +++ b/apps/sim/app/api/tools/neo4j/execute/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jExecuteContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -92,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { summary, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j execute failed:`, error) return NextResponse.json({ error: `Neo4j execute failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/neo4j/introspect/route.ts b/apps/sim/app/api/tools/neo4j/introspect/route.ts index f5aedff7c6..8d4fccc456 100644 --- a/apps/sim/app/api/tools/neo4j/introspect/route.ts +++ b/apps/sim/app/api/tools/neo4j/introspect/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jIntrospectContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -174,7 +175,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { indexes, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j introspection failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/neo4j/merge/route.ts b/apps/sim/app/api/tools/neo4j/merge/route.ts index 6764c48d10..032d897f8b 100644 --- a/apps/sim/app/api/tools/neo4j/merge/route.ts +++ b/apps/sim/app/api/tools/neo4j/merge/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jMergeContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -94,7 +95,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { summary, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j merge failed:`, error) return NextResponse.json({ error: `Neo4j merge failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/neo4j/query/route.ts b/apps/sim/app/api/tools/neo4j/query/route.ts index c1eee415a1..31550d4499 100644 --- a/apps/sim/app/api/tools/neo4j/query/route.ts +++ b/apps/sim/app/api/tools/neo4j/query/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jQueryContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -92,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { summary, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j query failed:`, error) return NextResponse.json({ error: `Neo4j query failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/neo4j/update/route.ts b/apps/sim/app/api/tools/neo4j/update/route.ts index 995dd6ecd9..f680eedbd8 100644 --- a/apps/sim/app/api/tools/neo4j/update/route.ts +++ b/apps/sim/app/api/tools/neo4j/update/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { neo4jUpdateContract } from '@/lib/api/contracts/tools/databases/neo4j' @@ -94,7 +95,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { summary, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Neo4j update failed:`, error) return NextResponse.json({ error: `Neo4j update failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/onedrive/download/route.ts b/apps/sim/app/api/tools/onedrive/download/route.ts index d90636e13f..d713208c49 100644 --- a/apps/sim/app/api/tools/onedrive/download/route.ts +++ b/apps/sim/app/api/tools/onedrive/download/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { onedriveDownloadContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -164,7 +165,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/onedrive/upload/route.ts b/apps/sim/app/api/tools/onedrive/upload/route.ts index 5913cff16e..f27cc9aa4b 100644 --- a/apps/sim/app/api/tools/onedrive/upload/route.ts +++ b/apps/sim/app/api/tools/onedrive/upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import * as XLSX from 'xlsx' import { onedriveUploadContract } from '@/lib/api/contracts/tools/microsoft' @@ -103,7 +104,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -119,7 +120,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to download file: ${getErrorMessage(error, 'Unknown error')}`, }, { status: 500 } ) @@ -380,7 +381,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { logger.error(`[${requestId}] Exception during Excel content write`, err) excelWriteResult = { success: false, - error: err instanceof Error ? err.message : 'Unknown error during Excel write', + error: getErrorMessage(err, 'Unknown error during Excel write'), } } } @@ -408,7 +409,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/onepassword/create-item/route.ts b/apps/sim/app/api/tools/onepassword/create-item/route.ts index b0ad6ea8bf..7785a02f3e 100644 --- a/apps/sim/app/api/tools/onepassword/create-item/route.ts +++ b/apps/sim/app/api/tools/onepassword/create-item/route.ts @@ -1,5 +1,6 @@ import type { ItemCreateParams } from '@1password/sdk' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordCreateItemContract } from '@/lib/api/contracts/tools/onepassword' @@ -98,7 +99,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Create item failed:`, error) return NextResponse.json({ error: `Failed to create item: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/delete-item/route.ts b/apps/sim/app/api/tools/onepassword/delete-item/route.ts index 223ad5c979..716e7a97fc 100644 --- a/apps/sim/app/api/tools/onepassword/delete-item/route.ts +++ b/apps/sim/app/api/tools/onepassword/delete-item/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordDeleteItemContract } from '@/lib/api/contracts/tools/onepassword' @@ -58,7 +59,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json({ success: true }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Delete item failed:`, error) return NextResponse.json({ error: `Failed to delete item: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/get-item/route.ts b/apps/sim/app/api/tools/onepassword/get-item/route.ts index 8aa473e6a8..9693a65d6f 100644 --- a/apps/sim/app/api/tools/onepassword/get-item/route.ts +++ b/apps/sim/app/api/tools/onepassword/get-item/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordGetItemContract } from '@/lib/api/contracts/tools/onepassword' @@ -63,7 +64,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Get item failed:`, error) return NextResponse.json({ error: `Failed to get item: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/get-vault/route.ts b/apps/sim/app/api/tools/onepassword/get-vault/route.ts index bc4139b452..474f7fc2a7 100644 --- a/apps/sim/app/api/tools/onepassword/get-vault/route.ts +++ b/apps/sim/app/api/tools/onepassword/get-vault/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordGetVaultContract } from '@/lib/api/contracts/tools/onepassword' @@ -67,7 +68,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Get vault failed:`, error) return NextResponse.json({ error: `Failed to get vault: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/list-items/route.ts b/apps/sim/app/api/tools/onepassword/list-items/route.ts index 4e512ac880..daeb42e807 100644 --- a/apps/sim/app/api/tools/onepassword/list-items/route.ts +++ b/apps/sim/app/api/tools/onepassword/list-items/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordListItemsContract } from '@/lib/api/contracts/tools/onepassword' @@ -75,7 +76,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] List items failed:`, error) return NextResponse.json({ error: `Failed to list items: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/list-vaults/route.ts b/apps/sim/app/api/tools/onepassword/list-vaults/route.ts index f7f1c4f3d5..fa4011daa7 100644 --- a/apps/sim/app/api/tools/onepassword/list-vaults/route.ts +++ b/apps/sim/app/api/tools/onepassword/list-vaults/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordListVaultsContract } from '@/lib/api/contracts/tools/onepassword' @@ -74,7 +75,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] List vaults failed:`, error) return NextResponse.json({ error: `Failed to list vaults: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/replace-item/route.ts b/apps/sim/app/api/tools/onepassword/replace-item/route.ts index 2a9a705fe1..0f2ee44b76 100644 --- a/apps/sim/app/api/tools/onepassword/replace-item/route.ts +++ b/apps/sim/app/api/tools/onepassword/replace-item/route.ts @@ -1,5 +1,6 @@ import type { Item } from '@1password/sdk' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordReplaceItemContract } from '@/lib/api/contracts/tools/onepassword' @@ -104,7 +105,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Replace item failed:`, error) return NextResponse.json({ error: `Failed to replace item: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/resolve-secret/route.ts b/apps/sim/app/api/tools/onepassword/resolve-secret/route.ts index 7df319bfe1..798b10b979 100644 --- a/apps/sim/app/api/tools/onepassword/resolve-secret/route.ts +++ b/apps/sim/app/api/tools/onepassword/resolve-secret/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordResolveSecretContract } from '@/lib/api/contracts/tools/onepassword' @@ -48,7 +49,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { reference: params.secretReference, }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Resolve secret failed:`, error) return NextResponse.json({ error: `Failed to resolve secret: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/onepassword/update-item/route.ts b/apps/sim/app/api/tools/onepassword/update-item/route.ts index d13f9c26f7..f027e95c45 100644 --- a/apps/sim/app/api/tools/onepassword/update-item/route.ts +++ b/apps/sim/app/api/tools/onepassword/update-item/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { onePasswordUpdateItemContract } from '@/lib/api/contracts/tools/onepassword' @@ -72,7 +73,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(data) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Update item failed:`, error) return NextResponse.json({ error: `Failed to update item: ${message}` }, { status: 500 }) } diff --git a/apps/sim/app/api/tools/outlook/copy/route.ts b/apps/sim/app/api/tools/outlook/copy/route.ts index f2e30c7986..406231899f 100644 --- a/apps/sim/app/api/tools/outlook/copy/route.ts +++ b/apps/sim/app/api/tools/outlook/copy/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookCopyContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -89,7 +90,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/outlook/delete/route.ts b/apps/sim/app/api/tools/outlook/delete/route.ts index 65b83b7966..1f3e4c1b5c 100644 --- a/apps/sim/app/api/tools/outlook/delete/route.ts +++ b/apps/sim/app/api/tools/outlook/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookDeleteContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -79,7 +80,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/outlook/draft/route.ts b/apps/sim/app/api/tools/outlook/draft/route.ts index 65bec69895..693f0fa2ad 100644 --- a/apps/sim/app/api/tools/outlook/draft/route.ts +++ b/apps/sim/app/api/tools/outlook/draft/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookDraftContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -116,7 +117,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -176,7 +177,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/outlook/mark-read/route.ts b/apps/sim/app/api/tools/outlook/mark-read/route.ts index cd749e6039..faf2ae20c5 100644 --- a/apps/sim/app/api/tools/outlook/mark-read/route.ts +++ b/apps/sim/app/api/tools/outlook/mark-read/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookMarkReadContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -89,7 +90,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/outlook/mark-unread/route.ts b/apps/sim/app/api/tools/outlook/mark-unread/route.ts index 932a6a21b5..e08caffa21 100644 --- a/apps/sim/app/api/tools/outlook/mark-unread/route.ts +++ b/apps/sim/app/api/tools/outlook/mark-unread/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookMarkUnreadContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -89,7 +90,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/outlook/move/route.ts b/apps/sim/app/api/tools/outlook/move/route.ts index 4949d71365..d27440ffe8 100644 --- a/apps/sim/app/api/tools/outlook/move/route.ts +++ b/apps/sim/app/api/tools/outlook/move/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookMoveContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -87,7 +88,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/outlook/send/route.ts b/apps/sim/app/api/tools/outlook/send/route.ts index cbd9a17578..080d9db487 100644 --- a/apps/sim/app/api/tools/outlook/send/route.ts +++ b/apps/sim/app/api/tools/outlook/send/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { outlookSendContract } from '@/lib/api/contracts/tools/microsoft' import { parseRequest } from '@/lib/api/server' @@ -116,7 +117,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -187,7 +188,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/pipedrive/get-files/route.ts b/apps/sim/app/api/tools/pipedrive/get-files/route.ts index 20a8de641d..bb6a05cea8 100644 --- a/apps/sim/app/api/tools/pipedrive/get-files/route.ts +++ b/apps/sim/app/api/tools/pipedrive/get-files/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { pipedriveGetFilesContract } from '@/lib/api/contracts/tools/pipedrive' import { parseRequest } from '@/lib/api/server' @@ -159,7 +160,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/postgresql/delete/route.ts b/apps/sim/app/api/tools/postgresql/delete/route.ts index e003c12e21..bdf9fcb605 100644 --- a/apps/sim/app/api/tools/postgresql/delete/route.ts +++ b/apps/sim/app/api/tools/postgresql/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { postgresqlDeleteContract } from '@/lib/api/contracts/tools/databases/postgresql' @@ -50,7 +51,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await sql.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] PostgreSQL delete failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/postgresql/execute/route.ts b/apps/sim/app/api/tools/postgresql/execute/route.ts index edbd757891..3a5bbd5388 100644 --- a/apps/sim/app/api/tools/postgresql/execute/route.ts +++ b/apps/sim/app/api/tools/postgresql/execute/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { postgresqlExecuteContract } from '@/lib/api/contracts/tools/databases/postgresql' @@ -63,7 +64,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await sql.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] PostgreSQL execute failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/postgresql/insert/route.ts b/apps/sim/app/api/tools/postgresql/insert/route.ts index 2509c12e96..760d26e1be 100644 --- a/apps/sim/app/api/tools/postgresql/insert/route.ts +++ b/apps/sim/app/api/tools/postgresql/insert/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { postgresqlInsertContract } from '@/lib/api/contracts/tools/databases/postgresql' @@ -50,7 +51,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await sql.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] PostgreSQL insert failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/postgresql/introspect/route.ts b/apps/sim/app/api/tools/postgresql/introspect/route.ts index 2290b3c006..0a295f93b9 100644 --- a/apps/sim/app/api/tools/postgresql/introspect/route.ts +++ b/apps/sim/app/api/tools/postgresql/introspect/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { postgresqlIntrospectContract } from '@/lib/api/contracts/tools/databases/postgresql' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await sql.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] PostgreSQL introspection failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/postgresql/query/route.ts b/apps/sim/app/api/tools/postgresql/query/route.ts index dd83495742..d47ad84189 100644 --- a/apps/sim/app/api/tools/postgresql/query/route.ts +++ b/apps/sim/app/api/tools/postgresql/query/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { postgresqlQueryContract } from '@/lib/api/contracts/tools/databases/postgresql' @@ -50,7 +51,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await sql.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] PostgreSQL query failed:`, error) return NextResponse.json({ error: `PostgreSQL query failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/postgresql/update/route.ts b/apps/sim/app/api/tools/postgresql/update/route.ts index 50559fe579..6533b47f5f 100644 --- a/apps/sim/app/api/tools/postgresql/update/route.ts +++ b/apps/sim/app/api/tools/postgresql/update/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { postgresqlUpdateContract } from '@/lib/api/contracts/tools/databases/postgresql' @@ -50,7 +51,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { await sql.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] PostgreSQL update failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/pulse/parse/route.ts b/apps/sim/app/api/tools/pulse/parse/route.ts index 081e9c20a8..ae8c550d29 100644 --- a/apps/sim/app/api/tools/pulse/parse/route.ts +++ b/apps/sim/app/api/tools/pulse/parse/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { pulseParseContract } from '@/lib/api/contracts/tools/media/document-parse' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -165,7 +166,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/quiver/image-to-svg/route.ts b/apps/sim/app/api/tools/quiver/image-to-svg/route.ts index 226e955d84..199dc8b0e7 100644 --- a/apps/sim/app/api/tools/quiver/image-to-svg/route.ts +++ b/apps/sim/app/api/tools/quiver/image-to-svg/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { quiverImageToSvgContract } from '@/lib/api/contracts/tools/quiver' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -156,7 +157,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error(`[${requestId}] Error in Quiver image-to-svg:`, error) - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') return NextResponse.json({ success: false, error: message }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/quiver/text-to-svg/route.ts b/apps/sim/app/api/tools/quiver/text-to-svg/route.ts index 11441e059d..5b12aaa067 100644 --- a/apps/sim/app/api/tools/quiver/text-to-svg/route.ts +++ b/apps/sim/app/api/tools/quiver/text-to-svg/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { quiverTextToSvgContract } from '@/lib/api/contracts/tools/quiver' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -149,7 +150,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error(`[${requestId}] Error in Quiver text-to-svg:`, error) - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') return NextResponse.json({ success: false, error: message }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/rds/delete/route.ts b/apps/sim/app/api/tools/rds/delete/route.ts index 982a2d4cd3..a83e5959c1 100644 --- a/apps/sim/app/api/tools/rds/delete/route.ts +++ b/apps/sim/app/api/tools/rds/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { rdsDeleteContract } from '@/lib/api/contracts/tools/databases/rds' @@ -54,7 +55,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] RDS delete failed:`, error) return NextResponse.json({ error: `RDS delete failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/rds/execute/route.ts b/apps/sim/app/api/tools/rds/execute/route.ts index 8573281083..408c5bfe29 100644 --- a/apps/sim/app/api/tools/rds/execute/route.ts +++ b/apps/sim/app/api/tools/rds/execute/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { rdsExecuteContract } from '@/lib/api/contracts/tools/databases/rds' @@ -53,7 +54,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] RDS execute failed:`, error) return NextResponse.json({ error: `RDS execute failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/rds/insert/route.ts b/apps/sim/app/api/tools/rds/insert/route.ts index 42d18d4dba..ae7751adf6 100644 --- a/apps/sim/app/api/tools/rds/insert/route.ts +++ b/apps/sim/app/api/tools/rds/insert/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { rdsInsertContract } from '@/lib/api/contracts/tools/databases/rds' @@ -54,7 +55,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] RDS insert failed:`, error) return NextResponse.json({ error: `RDS insert failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/rds/introspect/route.ts b/apps/sim/app/api/tools/rds/introspect/route.ts index 91a18f9787..32e3df1059 100644 --- a/apps/sim/app/api/tools/rds/introspect/route.ts +++ b/apps/sim/app/api/tools/rds/introspect/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { rdsIntrospectContract } from '@/lib/api/contracts/tools/databases/rds' @@ -59,7 +60,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] RDS introspection failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/rds/query/route.ts b/apps/sim/app/api/tools/rds/query/route.ts index 1f71bbf68e..cc51112284 100644 --- a/apps/sim/app/api/tools/rds/query/route.ts +++ b/apps/sim/app/api/tools/rds/query/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { rdsQueryContract } from '@/lib/api/contracts/tools/databases/rds' @@ -59,7 +60,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] RDS query failed:`, error) return NextResponse.json({ error: `RDS query failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/rds/update/route.ts b/apps/sim/app/api/tools/rds/update/route.ts index 5b8b5c409c..6ea2df5c10 100644 --- a/apps/sim/app/api/tools/rds/update/route.ts +++ b/apps/sim/app/api/tools/rds/update/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { rdsUpdateContract } from '@/lib/api/contracts/tools/databases/rds' @@ -55,7 +56,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] RDS update failed:`, error) return NextResponse.json({ error: `RDS update failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/redis/execute/route.ts b/apps/sim/app/api/tools/redis/execute/route.ts index 9022229b0c..c3fb36c85b 100644 --- a/apps/sim/app/api/tools/redis/execute/route.ts +++ b/apps/sim/app/api/tools/redis/execute/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import Redis from 'ioredis' import { type NextRequest, NextResponse } from 'next/server' import { redisExecuteContract } from '@/lib/api/contracts/tools/databases/redis' @@ -54,7 +55,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json({ result }) } catch (error) { logger.error('Redis command failed', { error }) - const errorMessage = error instanceof Error ? error.message : 'Redis command failed' + const errorMessage = getErrorMessage(error, 'Redis command failed') return NextResponse.json({ error: errorMessage }, { status: 500 }) } finally { if (client) { diff --git a/apps/sim/app/api/tools/reducto/parse/route.ts b/apps/sim/app/api/tools/reducto/parse/route.ts index 79a864f661..91a1ea440f 100644 --- a/apps/sim/app/api/tools/reducto/parse/route.ts +++ b/apps/sim/app/api/tools/reducto/parse/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { reductoParseContract } from '@/lib/api/contracts/tools/media/document-parse' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -162,7 +163,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/s3/copy-object/route.ts b/apps/sim/app/api/tools/s3/copy-object/route.ts index 5feb4b7b64..28a71cf12d 100644 --- a/apps/sim/app/api/tools/s3/copy-object/route.ts +++ b/apps/sim/app/api/tools/s3/copy-object/route.ts @@ -1,5 +1,6 @@ import { CopyObjectCommand, type ObjectCannedACL, S3Client } from '@aws-sdk/client-s3' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsS3CopyObjectContract } from '@/lib/api/contracts/tools/aws/s3-copy-object' import { parseToolRequest } from '@/lib/api/server' @@ -92,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/s3/delete-object/route.ts b/apps/sim/app/api/tools/s3/delete-object/route.ts index 2c74a23240..29a5778b6e 100644 --- a/apps/sim/app/api/tools/s3/delete-object/route.ts +++ b/apps/sim/app/api/tools/s3/delete-object/route.ts @@ -1,5 +1,6 @@ import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsS3DeleteObjectContract } from '@/lib/api/contracts/tools/aws/s3-delete-object' import { parseToolRequest } from '@/lib/api/server' @@ -84,7 +85,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/s3/list-objects/route.ts b/apps/sim/app/api/tools/s3/list-objects/route.ts index af447a6d0e..0f2ad91467 100644 --- a/apps/sim/app/api/tools/s3/list-objects/route.ts +++ b/apps/sim/app/api/tools/s3/list-objects/route.ts @@ -1,5 +1,6 @@ import { ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsS3ListObjectsContract } from '@/lib/api/contracts/tools/aws/s3-list-objects' import { parseToolRequest } from '@/lib/api/server' @@ -92,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/s3/put-object/route.ts b/apps/sim/app/api/tools/s3/put-object/route.ts index a2798c4fc1..13d543aca0 100644 --- a/apps/sim/app/api/tools/s3/put-object/route.ts +++ b/apps/sim/app/api/tools/s3/put-object/route.ts @@ -1,5 +1,6 @@ import { type ObjectCannedACL, PutObjectCommand, S3Client } from '@aws-sdk/client-s3' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { awsS3PutObjectContract } from '@/lib/api/contracts/tools/aws/s3-put-object' import { parseToolRequest } from '@/lib/api/server' @@ -71,7 +72,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -134,7 +135,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/secrets_manager/create-secret/route.ts b/apps/sim/app/api/tools/secrets_manager/create-secret/route.ts index dcf851b127..cf38ee596b 100644 --- a/apps/sim/app/api/tools/secrets_manager/create-secret/route.ts +++ b/apps/sim/app/api/tools/secrets_manager/create-secret/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { awsSecretsManagerCreateSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-create-secret' @@ -46,7 +47,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Failed to create secret:`, error) return NextResponse.json({ error: `Failed to create secret: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/secrets_manager/delete-secret/route.ts b/apps/sim/app/api/tools/secrets_manager/delete-secret/route.ts index 11eb832b3d..62cdb9ffd5 100644 --- a/apps/sim/app/api/tools/secrets_manager/delete-secret/route.ts +++ b/apps/sim/app/api/tools/secrets_manager/delete-secret/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { awsSecretsManagerDeleteSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-delete-secret' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Failed to delete secret:`, error) return NextResponse.json({ error: `Failed to delete secret: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/secrets_manager/get-secret/route.ts b/apps/sim/app/api/tools/secrets_manager/get-secret/route.ts index daa5846e85..31b0ba266c 100644 --- a/apps/sim/app/api/tools/secrets_manager/get-secret/route.ts +++ b/apps/sim/app/api/tools/secrets_manager/get-secret/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { awsSecretsManagerGetSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-get-secret' @@ -48,7 +49,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Failed to retrieve secret:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/secrets_manager/list-secrets/route.ts b/apps/sim/app/api/tools/secrets_manager/list-secrets/route.ts index 775cb6b982..6345b07b12 100644 --- a/apps/sim/app/api/tools/secrets_manager/list-secrets/route.ts +++ b/apps/sim/app/api/tools/secrets_manager/list-secrets/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { awsSecretsManagerListSecretsContract } from '@/lib/api/contracts/tools/aws/secrets-manager-list-secrets' @@ -43,7 +44,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Failed to list secrets:`, error) return NextResponse.json({ error: `Failed to list secrets: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/secrets_manager/update-secret/route.ts b/apps/sim/app/api/tools/secrets_manager/update-secret/route.ts index d6ebaa502a..1bb386bf56 100644 --- a/apps/sim/app/api/tools/secrets_manager/update-secret/route.ts +++ b/apps/sim/app/api/tools/secrets_manager/update-secret/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { awsSecretsManagerUpdateSecretContract } from '@/lib/api/contracts/tools/aws/secrets-manager-update-secret' @@ -51,7 +52,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] Failed to update secret:`, error) return NextResponse.json({ error: `Failed to update secret: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/sendgrid/send-mail/route.ts b/apps/sim/app/api/tools/sendgrid/send-mail/route.ts index 5c4c13952e..b70144b195 100644 --- a/apps/sim/app/api/tools/sendgrid/send-mail/route.ts +++ b/apps/sim/app/api/tools/sendgrid/send-mail/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { sendGridSendMailContract } from '@/lib/api/contracts/tools/communication/email' import { parseRequest } from '@/lib/api/server' @@ -115,7 +116,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) @@ -165,7 +166,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Unexpected error:`, error) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/sftp/delete/route.ts b/apps/sim/app/api/tools/sftp/delete/route.ts index 5109230a6a..42e260dfe2 100644 --- a/apps/sim/app/api/tools/sftp/delete/route.ts +++ b/apps/sim/app/api/tools/sftp/delete/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import type { SFTPWrapper } from 'ssh2' import { sftpDeleteContract } from '@/lib/api/contracts/storage-transfer' @@ -157,7 +158,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SFTP delete failed:`, error) return NextResponse.json({ error: `SFTP delete failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/sftp/download/route.ts b/apps/sim/app/api/tools/sftp/download/route.ts index 73aef7522e..5ba713c106 100644 --- a/apps/sim/app/api/tools/sftp/download/route.ts +++ b/apps/sim/app/api/tools/sftp/download/route.ts @@ -1,5 +1,6 @@ import path from 'path' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { sftpDownloadContract } from '@/lib/api/contracts/storage-transfer' import { parseRequest } from '@/lib/api/server' @@ -127,7 +128,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SFTP download failed:`, error) return NextResponse.json({ error: `SFTP download failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/sftp/mkdir/route.ts b/apps/sim/app/api/tools/sftp/mkdir/route.ts index 6dbb16c49b..122568e791 100644 --- a/apps/sim/app/api/tools/sftp/mkdir/route.ts +++ b/apps/sim/app/api/tools/sftp/mkdir/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import type { SFTPWrapper } from 'ssh2' import { sftpMkdirContract } from '@/lib/api/contracts/storage-transfer' @@ -137,7 +138,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SFTP mkdir failed:`, error) return NextResponse.json({ error: `SFTP mkdir failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/sftp/upload/route.ts b/apps/sim/app/api/tools/sftp/upload/route.ts index b7198ee436..6b686d57c2 100644 --- a/apps/sim/app/api/tools/sftp/upload/route.ts +++ b/apps/sim/app/api/tools/sftp/upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { sftpUploadContract } from '@/lib/api/contracts/storage-transfer' import { parseRequest } from '@/lib/api/server' @@ -143,9 +144,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to upload file ${file.name}:`, error) throw new Error( - `Failed to upload file "${file.name}": ${ - error instanceof Error ? error.message : 'Unknown error' - }` + `Failed to upload file "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } } @@ -210,7 +209,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SFTP upload failed:`, error) return NextResponse.json({ error: `SFTP upload failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/slack/add-reaction/route.ts b/apps/sim/app/api/tools/slack/add-reaction/route.ts index b91f98f895..a266f890b7 100644 --- a/apps/sim/app/api/tools/slack/add-reaction/route.ts +++ b/apps/sim/app/api/tools/slack/add-reaction/route.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackAddReactionContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -64,7 +65,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/delete-message/route.ts b/apps/sim/app/api/tools/slack/delete-message/route.ts index c143c2e161..4634a3da07 100644 --- a/apps/sim/app/api/tools/slack/delete-message/route.ts +++ b/apps/sim/app/api/tools/slack/delete-message/route.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackDeleteMessageContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -62,7 +63,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/download/route.ts b/apps/sim/app/api/tools/slack/download/route.ts index 06756647d0..2cc356bef6 100644 --- a/apps/sim/app/api/tools/slack/download/route.ts +++ b/apps/sim/app/api/tools/slack/download/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackDownloadContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -157,7 +158,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/read-messages/route.ts b/apps/sim/app/api/tools/slack/read-messages/route.ts index bceb2a989b..5960b6f20d 100644 --- a/apps/sim/app/api/tools/slack/read-messages/route.ts +++ b/apps/sim/app/api/tools/slack/read-messages/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackReadMessagesContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -177,7 +178,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/remove-reaction/route.ts b/apps/sim/app/api/tools/slack/remove-reaction/route.ts index 0a68633552..108d08b52e 100644 --- a/apps/sim/app/api/tools/slack/remove-reaction/route.ts +++ b/apps/sim/app/api/tools/slack/remove-reaction/route.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackRemoveReactionContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -64,7 +65,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/send-ephemeral/route.ts b/apps/sim/app/api/tools/slack/send-ephemeral/route.ts index 8ae56bcdeb..058ce45d0d 100644 --- a/apps/sim/app/api/tools/slack/send-ephemeral/route.ts +++ b/apps/sim/app/api/tools/slack/send-ephemeral/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackSendEphemeralContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -82,7 +83,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/send-message/route.ts b/apps/sim/app/api/tools/slack/send-message/route.ts index 7745d22c51..a407df3bbb 100644 --- a/apps/sim/app/api/tools/slack/send-message/route.ts +++ b/apps/sim/app/api/tools/slack/send-message/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackSendMessageContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -75,7 +76,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/slack/update-message/route.ts b/apps/sim/app/api/tools/slack/update-message/route.ts index 4f3036d47a..fdfd675cae 100644 --- a/apps/sim/app/api/tools/slack/update-message/route.ts +++ b/apps/sim/app/api/tools/slack/update-message/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { slackUpdateMessageContract } from '@/lib/api/contracts/tools/communication/slack' import { parseRequest } from '@/lib/api/server' @@ -100,7 +101,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/smtp/send/route.ts b/apps/sim/app/api/tools/smtp/send/route.ts index 499768b407..e08101c859 100644 --- a/apps/sim/app/api/tools/smtp/send/route.ts +++ b/apps/sim/app/api/tools/smtp/send/route.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import nodemailer from 'nodemailer' import { smtpSendContract } from '@/lib/api/contracts/tools/communication/email' @@ -137,7 +137,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Failed to download attachment ${file.name}:`, error) throw new Error( - `Failed to download attachment "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to download attachment "${file.name}": ${getErrorMessage(error, 'Unknown error')}` ) } }) diff --git a/apps/sim/app/api/tools/sqs/send/route.ts b/apps/sim/app/api/tools/sqs/send/route.ts index fc9b74db01..497cac1d99 100644 --- a/apps/sim/app/api/tools/sqs/send/route.ts +++ b/apps/sim/app/api/tools/sqs/send/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { awsSqsSendContract } from '@/lib/api/contracts/tools/aws/sqs-send' @@ -52,7 +53,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.destroy() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SQS send message failed:`, error) return NextResponse.json({ error: `SQS send message failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/ssh/check-command-exists/route.ts b/apps/sim/app/api/tools/ssh/check-command-exists/route.ts index c297342db7..ea0238b01f 100644 --- a/apps/sim/app/api/tools/ssh/check-command-exists/route.ts +++ b/apps/sim/app/api/tools/ssh/check-command-exists/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshCheckCommandExistsContract } from '@/lib/api/contracts/storage-transfer' @@ -78,7 +79,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH check command exists failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/check-file-exists/route.ts b/apps/sim/app/api/tools/ssh/check-file-exists/route.ts index fc2a046e76..b5aaf72a62 100644 --- a/apps/sim/app/api/tools/ssh/check-file-exists/route.ts +++ b/apps/sim/app/api/tools/ssh/check-file-exists/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper, Stats } from 'ssh2' @@ -106,7 +107,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH check file exists failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/create-directory/route.ts b/apps/sim/app/api/tools/ssh/create-directory/route.ts index 7dcb94522e..abd3214d7f 100644 --- a/apps/sim/app/api/tools/ssh/create-directory/route.ts +++ b/apps/sim/app/api/tools/ssh/create-directory/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshCreateDirectoryContract } from '@/lib/api/contracts/storage-transfer' @@ -79,7 +80,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH create directory failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/delete-file/route.ts b/apps/sim/app/api/tools/ssh/delete-file/route.ts index a08ea88543..52b1c714c8 100644 --- a/apps/sim/app/api/tools/ssh/delete-file/route.ts +++ b/apps/sim/app/api/tools/ssh/delete-file/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshDeleteFileContract } from '@/lib/api/contracts/storage-transfer' @@ -75,7 +76,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH delete file failed:`, error) return NextResponse.json({ error: `SSH delete file failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/ssh/download-file/route.ts b/apps/sim/app/api/tools/ssh/download-file/route.ts index 01f2848182..8bd41d9f25 100644 --- a/apps/sim/app/api/tools/ssh/download-file/route.ts +++ b/apps/sim/app/api/tools/ssh/download-file/route.ts @@ -1,5 +1,6 @@ import path from 'path' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' @@ -119,7 +120,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH file download failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/execute-command/route.ts b/apps/sim/app/api/tools/ssh/execute-command/route.ts index 571c71bb30..3b192957d9 100644 --- a/apps/sim/app/api/tools/ssh/execute-command/route.ts +++ b/apps/sim/app/api/tools/ssh/execute-command/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshExecuteCommandContract } from '@/lib/api/contracts/storage-transfer' @@ -61,7 +62,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH command execution failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/execute-script/route.ts b/apps/sim/app/api/tools/ssh/execute-script/route.ts index 9da4f51027..ce6cf309c7 100644 --- a/apps/sim/app/api/tools/ssh/execute-script/route.ts +++ b/apps/sim/app/api/tools/ssh/execute-script/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshExecuteScriptContract } from '@/lib/api/contracts/storage-transfer' @@ -73,7 +74,7 @@ exit $exit_code` client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH script execution failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/get-system-info/route.ts b/apps/sim/app/api/tools/ssh/get-system-info/route.ts index 77b148a70d..9dc315afeb 100644 --- a/apps/sim/app/api/tools/ssh/get-system-info/route.ts +++ b/apps/sim/app/api/tools/ssh/get-system-info/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshGetSystemInfoContract } from '@/lib/api/contracts/storage-transfer' @@ -99,7 +100,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH get system info failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/list-directory/route.ts b/apps/sim/app/api/tools/ssh/list-directory/route.ts index c3bb0e32d1..b8d4b79561 100644 --- a/apps/sim/app/api/tools/ssh/list-directory/route.ts +++ b/apps/sim/app/api/tools/ssh/list-directory/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import type { Client, FileEntry, SFTPWrapper } from 'ssh2' @@ -103,7 +104,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH list directory failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/move-rename/route.ts b/apps/sim/app/api/tools/ssh/move-rename/route.ts index cf9114ea20..2433bdd08f 100644 --- a/apps/sim/app/api/tools/ssh/move-rename/route.ts +++ b/apps/sim/app/api/tools/ssh/move-rename/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { sshMoveRenameContract } from '@/lib/api/contracts/storage-transfer' @@ -92,7 +93,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH move/rename failed:`, error) return NextResponse.json({ error: `SSH move/rename failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/ssh/read-file-content/route.ts b/apps/sim/app/api/tools/ssh/read-file-content/route.ts index 778bbc1f63..2558f1fcf6 100644 --- a/apps/sim/app/api/tools/ssh/read-file-content/route.ts +++ b/apps/sim/app/api/tools/ssh/read-file-content/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' @@ -111,7 +112,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH read file content failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/ssh/upload-file/route.ts b/apps/sim/app/api/tools/ssh/upload-file/route.ts index c85e6418c0..106aeff7c7 100644 --- a/apps/sim/app/api/tools/ssh/upload-file/route.ts +++ b/apps/sim/app/api/tools/ssh/upload-file/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' @@ -102,7 +103,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH file upload failed:`, error) return NextResponse.json({ error: `SSH file upload failed: ${errorMessage}` }, { status: 500 }) diff --git a/apps/sim/app/api/tools/ssh/write-file-content/route.ts b/apps/sim/app/api/tools/ssh/write-file-content/route.ts index 4dcab77ea2..9ce1f92b5e 100644 --- a/apps/sim/app/api/tools/ssh/write-file-content/route.ts +++ b/apps/sim/app/api/tools/ssh/write-file-content/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' @@ -122,7 +123,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { client.end() } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' + const errorMessage = getErrorMessage(error, 'Unknown error occurred') logger.error(`[${requestId}] SSH write file content failed:`, error) return NextResponse.json( diff --git a/apps/sim/app/api/tools/stagehand/agent/route.ts b/apps/sim/app/api/tools/stagehand/agent/route.ts index 8b0842d597..a1fa2ce770 100644 --- a/apps/sim/app/api/tools/stagehand/agent/route.ts +++ b/apps/sim/app/api/tools/stagehand/agent/route.ts @@ -1,5 +1,6 @@ import type { Stagehand as StagehandType } from '@browserbasehq/stagehand' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { stagehandAgentContract } from '@/lib/api/contracts/tools/stagehand' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -330,7 +331,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error('Stagehand agent execution error', { error, - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) @@ -369,13 +370,13 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error('Unexpected error in agent API route', { error, - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) return NextResponse.json( { error: 'Internal server error', - details: error instanceof Error ? error.message : 'Unknown error', + details: getErrorMessage(error, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/stagehand/extract/route.ts b/apps/sim/app/api/tools/stagehand/extract/route.ts index 5d04955996..c72f96c4e7 100644 --- a/apps/sim/app/api/tools/stagehand/extract/route.ts +++ b/apps/sim/app/api/tools/stagehand/extract/route.ts @@ -1,5 +1,6 @@ import type { Stagehand as StagehandType } from '@browserbasehq/stagehand' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { stagehandExtractContract } from '@/lib/api/contracts/tools/stagehand' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -149,7 +150,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (schemaError) { logger.error('Failed to convert JSON schema to Zod schema', { error: schemaError, - message: schemaError instanceof Error ? schemaError.message : 'Unknown schema error', + message: getErrorMessage(schemaError, 'Unknown schema error'), }) logger.info('Falling back to simple extraction without schema') @@ -181,15 +182,14 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (extractError) { logger.error('Error during extraction operation', { error: extractError, - message: - extractError instanceof Error ? extractError.message : 'Unknown extraction error', + message: getErrorMessage(extractError, 'Unknown extraction error'), }) throw extractError } } catch (error) { logger.error('Stagehand extraction error', { error, - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) @@ -226,13 +226,13 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error('Unexpected error in extraction API route', { error, - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) return NextResponse.json( { error: 'Internal server error', - details: error instanceof Error ? error.message : 'Unknown error', + details: getErrorMessage(error, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/stt/route.ts b/apps/sim/app/api/tools/stt/route.ts index 44152a4ad5..a320bcce00 100644 --- a/apps/sim/app/api/tools/stt/route.ts +++ b/apps/sim/app/api/tools/stt/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' @@ -176,7 +177,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { logger.error(`[${requestId}] Video extraction failed:`, error) return NextResponse.json( { - error: `Failed to extract audio from video: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to extract audio from video: ${getErrorMessage(error, 'Unknown error')}`, }, { status: 500 } ) @@ -273,7 +274,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } } catch (error) { logger.error(`[${requestId}] Transcription failed:`, error) - const errorMessage = error instanceof Error ? error.message : 'Transcription failed' + const errorMessage = getErrorMessage(error, 'Transcription failed') return NextResponse.json({ error: errorMessage }, { status: 500 }) } @@ -291,7 +292,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(response) } catch (error) { logger.error(`[${requestId}] STT proxy error:`, error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') return NextResponse.json({ error: errorMessage }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/supabase/storage-upload/route.ts b/apps/sim/app/api/tools/supabase/storage-upload/route.ts index 3e9a1ee864..e36f88c501 100644 --- a/apps/sim/app/api/tools/supabase/storage-upload/route.ts +++ b/apps/sim/app/api/tools/supabase/storage-upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { supabaseStorageUploadContract } from '@/lib/api/contracts/tools/databases/supabase' import { parseToolRequest } from '@/lib/api/server' @@ -138,7 +139,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -241,7 +242,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/telegram/send-document/route.ts b/apps/sim/app/api/tools/telegram/send-document/route.ts index c9c51c7003..2d685c501f 100644 --- a/apps/sim/app/api/tools/telegram/send-document/route.ts +++ b/apps/sim/app/api/tools/telegram/send-document/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { telegramSendDocumentContract } from '@/lib/api/contracts/tools/communication/messaging' import { parseRequest } from '@/lib/api/server' @@ -151,7 +152,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/textract/parse/route.ts b/apps/sim/app/api/tools/textract/parse/route.ts index 209bb01557..48e6f07899 100644 --- a/apps/sim/app/api/tools/textract/parse/route.ts +++ b/apps/sim/app/api/tools/textract/parse/route.ts @@ -1,5 +1,6 @@ import crypto from 'crypto' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { type NextRequest, NextResponse } from 'next/server' import { textractParseContract } from '@/lib/api/contracts/tools/media/document-parse' @@ -423,7 +424,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -615,7 +616,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/tts/route.ts b/apps/sim/app/api/tools/tts/route.ts index 3170c3da4a..7a82e57eeb 100644 --- a/apps/sim/app/api/tools/tts/route.ts +++ b/apps/sim/app/api/tools/tts/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { ttsToolContract } from '@/lib/api/contracts/tools/media/tts' @@ -139,7 +140,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { - error: `Internal Server Error: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Internal Server Error: ${getErrorMessage(error, 'Unknown error')}`, }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/tts/unified/route.ts b/apps/sim/app/api/tools/tts/unified/route.ts index ca7186e9b9..3b6f0a7870 100644 --- a/apps/sim/app/api/tools/tts/unified/route.ts +++ b/apps/sim/app/api/tools/tts/unified/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' @@ -206,7 +207,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } } catch (error) { logger.error(`[${requestId}] TTS synthesis failed:`, error) - const errorMessage = error instanceof Error ? error.message : 'TTS synthesis failed' + const errorMessage = getErrorMessage(error, 'TTS synthesis failed') return NextResponse.json({ error: errorMessage }, { status: 500 }) } @@ -275,7 +276,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(response) } catch (error) { logger.error(`[${requestId}] TTS unified proxy error:`, error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') return NextResponse.json({ error: errorMessage }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/twilio/get-recording/route.ts b/apps/sim/app/api/tools/twilio/get-recording/route.ts index 92cbece930..ddd0fc9350 100644 --- a/apps/sim/app/api/tools/twilio/get-recording/route.ts +++ b/apps/sim/app/api/tools/twilio/get-recording/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { twilioGetRecordingContract } from '@/lib/api/contracts/tools/communication/messaging' import { parseRequest } from '@/lib/api/server' @@ -237,7 +238,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/video/route.ts b/apps/sim/app/api/tools/video/route.ts index f5f7969ff2..84d930d9c7 100644 --- a/apps/sim/app/api/tools/video/route.ts +++ b/apps/sim/app/api/tools/video/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' @@ -205,7 +206,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } } catch (error) { logger.error(`[${requestId}] Video generation failed:`, error) - const errorMessage = error instanceof Error ? error.message : 'Video generation failed' + const errorMessage = getErrorMessage(error, 'Video generation failed') return NextResponse.json({ error: errorMessage }, { status: 500 }) } @@ -242,9 +243,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error(`[${requestId}] Failed to upload video file:`, error) - throw new Error( - `Failed to store video: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to store video: ${getErrorMessage(error, 'Unknown error')}`) } return NextResponse.json({ @@ -275,9 +274,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { videoUrl = `${getBaseUrl()}${fileInfo.path}` } catch (error) { logger.error(`[${requestId}] Failed to upload video file (fallback):`, error) - throw new Error( - `Failed to store video: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to store video: ${getErrorMessage(error, 'Unknown error')}`) } logger.info(`[${requestId}] Video generation completed successfully`) @@ -293,7 +290,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error(`[${requestId}] Video proxy error:`, error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') return NextResponse.json({ error: errorMessage }, { status: 500 }) } }) diff --git a/apps/sim/app/api/tools/vision/analyze/route.ts b/apps/sim/app/api/tools/vision/analyze/route.ts index 4779dcb86f..70b106e3c6 100644 --- a/apps/sim/app/api/tools/vision/analyze/route.ts +++ b/apps/sim/app/api/tools/vision/analyze/route.ts @@ -1,5 +1,6 @@ import { GoogleGenAI } from '@google/genai' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { visionAnalyzeContract } from '@/lib/api/contracts/tools/media/vision' import { parseRequest } from '@/lib/api/server' @@ -79,7 +80,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process image file', + error: getErrorMessage(error, 'Failed to process image file'), }, { status: 400 } ) @@ -360,7 +361,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/wordpress/upload/route.ts b/apps/sim/app/api/tools/wordpress/upload/route.ts index 5b9a1cf938..a62632caea 100644 --- a/apps/sim/app/api/tools/wordpress/upload/route.ts +++ b/apps/sim/app/api/tools/wordpress/upload/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { wordpressUploadContract } from '@/lib/api/contracts/storage-transfer' import { parseRequest } from '@/lib/api/server' @@ -72,7 +73,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to process file', + error: getErrorMessage(error, 'Failed to process file'), }, { status: 400 } ) @@ -96,7 +97,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`, + error: `Failed to download file: ${getErrorMessage(error, 'Unknown error')}`, }, { status: 500 } ) @@ -195,7 +196,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/tools/workday/assign-onboarding/route.ts b/apps/sim/app/api/tools/workday/assign-onboarding/route.ts index 044dc7c662..96879dcc14 100644 --- a/apps/sim/app/api/tools/workday/assign-onboarding/route.ts +++ b/apps/sim/app/api/tools/workday/assign-onboarding/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayAssignOnboardingContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -53,7 +54,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday assign onboarding failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/change-job/route.ts b/apps/sim/app/api/tools/workday/change-job/route.ts index 897124374a..8cbba58fe1 100644 --- a/apps/sim/app/api/tools/workday/change-job/route.ts +++ b/apps/sim/app/api/tools/workday/change-job/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayChangeJobContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -76,7 +77,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday change job failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/create-prehire/route.ts b/apps/sim/app/api/tools/workday/create-prehire/route.ts index fae29cdd05..7ed61b992f 100644 --- a/apps/sim/app/api/tools/workday/create-prehire/route.ts +++ b/apps/sim/app/api/tools/workday/create-prehire/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayCreatePrehireContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -118,7 +119,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday create prehire failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/get-compensation/route.ts b/apps/sim/app/api/tools/workday/get-compensation/route.ts index 9ed21ecdc6..eb57a04418 100644 --- a/apps/sim/app/api/tools/workday/get-compensation/route.ts +++ b/apps/sim/app/api/tools/workday/get-compensation/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayGetCompensationContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -94,7 +95,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday get compensation failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/get-organizations/route.ts b/apps/sim/app/api/tools/workday/get-organizations/route.ts index adbf530424..7758ec415b 100644 --- a/apps/sim/app/api/tools/workday/get-organizations/route.ts +++ b/apps/sim/app/api/tools/workday/get-organizations/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayGetOrganizationsContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -85,7 +86,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday get organizations failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/get-worker/route.ts b/apps/sim/app/api/tools/workday/get-worker/route.ts index 7b44467fe0..96fe04b884 100644 --- a/apps/sim/app/api/tools/workday/get-worker/route.ts +++ b/apps/sim/app/api/tools/workday/get-worker/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayGetWorkerContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -75,7 +76,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday get worker failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/hire/route.ts b/apps/sim/app/api/tools/workday/hire/route.ts index 28d2f77663..9bc24a6b74 100644 --- a/apps/sim/app/api/tools/workday/hire/route.ts +++ b/apps/sim/app/api/tools/workday/hire/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayHireContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -63,7 +64,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday hire employee failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/list-workers/route.ts b/apps/sim/app/api/tools/workday/list-workers/route.ts index 2d7f943d47..878a4048f1 100644 --- a/apps/sim/app/api/tools/workday/list-workers/route.ts +++ b/apps/sim/app/api/tools/workday/list-workers/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayListWorkersContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -71,7 +72,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday list workers failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/terminate/route.ts b/apps/sim/app/api/tools/workday/terminate/route.ts index a4fb4b0995..9548af4467 100644 --- a/apps/sim/app/api/tools/workday/terminate/route.ts +++ b/apps/sim/app/api/tools/workday/terminate/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayTerminateContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -61,7 +62,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday terminate employee failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/workday/update-worker/route.ts b/apps/sim/app/api/tools/workday/update-worker/route.ts index 0486d88d0c..e3681a2567 100644 --- a/apps/sim/app/api/tools/workday/update-worker/route.ts +++ b/apps/sim/app/api/tools/workday/update-worker/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workdayUpdateWorkerContract } from '@/lib/api/contracts/tools/workday' import { parseRequest } from '@/lib/api/server' @@ -53,7 +54,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { } catch (error) { logger.error(`[${requestId}] Workday update worker failed`, { error }) return NextResponse.json( - { success: false, error: error instanceof Error ? error.message : 'Unknown error' }, + { success: false, error: getErrorMessage(error, 'Unknown error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/tools/zoom/get-recordings/route.ts b/apps/sim/app/api/tools/zoom/get-recordings/route.ts index 6e19b24466..12da2faa42 100644 --- a/apps/sim/app/api/tools/zoom/get-recordings/route.ts +++ b/apps/sim/app/api/tools/zoom/get-recordings/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { zoomGetRecordingsContract } from '@/lib/api/contracts/tools/zoom' import { parseRequest } from '@/lib/api/server' @@ -202,7 +203,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Unknown error occurred', + error: getErrorMessage(error, 'Unknown error occurred'), }, { status: 500 } ) diff --git a/apps/sim/app/api/users/me/usage-limits/route.ts b/apps/sim/app/api/users/me/usage-limits/route.ts index a5214731e5..4dfd60b1a0 100644 --- a/apps/sim/app/api/users/me/usage-limits/route.ts +++ b/apps/sim/app/api/users/me/usage-limits/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { usageLimitsRequestSchema } from '@/lib/api/contracts/usage-limits' import { AuthType, checkHybridAuth } from '@/lib/auth/hybrid' @@ -81,9 +82,6 @@ export const GET = withRouteHandler(async (request: NextRequest) => { }) } catch (error) { logger.error('Error checking usage limits:', error) - return createErrorResponse( - error instanceof Error ? error.message : 'Failed to check usage limits', - 500 - ) + return createErrorResponse(getErrorMessage(error, 'Failed to check usage limits'), 500) } }) diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts index 457445a87c..82bdd359d5 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts @@ -26,6 +26,7 @@ import { db } from '@sim/db' import { workflow, workflowFolder } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { NextResponse } from 'next/server' @@ -310,7 +311,7 @@ async function importSingleWorkflow( workflowId: '', name: wf.name, success: false, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), } } } diff --git a/apps/sim/app/api/v1/audit-logs/[id]/route.ts b/apps/sim/app/api/v1/audit-logs/[id]/route.ts index 43aa1991cf..3c7208cc84 100644 --- a/apps/sim/app/api/v1/audit-logs/[id]/route.ts +++ b/apps/sim/app/api/v1/audit-logs/[id]/route.ts @@ -13,6 +13,7 @@ import { db } from '@sim/db' import { auditLog, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq, inArray, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -82,7 +83,7 @@ export const GET = withRouteHandler( return NextResponse.json(response.body, { headers: response.headers }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Audit log detail fetch error`, { error: message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/v1/audit-logs/route.ts b/apps/sim/app/api/v1/audit-logs/route.ts index 2e22d64bc1..0ac8b9ab43 100644 --- a/apps/sim/app/api/v1/audit-logs/route.ts +++ b/apps/sim/app/api/v1/audit-logs/route.ts @@ -20,6 +20,7 @@ */ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { v1ListAuditLogsContract } from '@/lib/api/contracts/v1/audit-logs' @@ -108,7 +109,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(response.body, { headers: response.headers }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Audit logs fetch error`, { error: message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/v1/files/route.ts b/apps/sim/app/api/v1/files/route.ts index f08b960fe8..a286a655de 100644 --- a/apps/sim/app/api/v1/files/route.ts +++ b/apps/sim/app/api/v1/files/route.ts @@ -1,5 +1,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { v1ListFilesContract, v1UploadFileFormFieldsSchema } from '@/lib/api/contracts/v1/files' import { getValidationErrorMessage, parseRequest } from '@/lib/api/server' @@ -171,7 +172,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { }, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to upload file' + const errorMessage = getErrorMessage(error, 'Failed to upload file') const isDuplicate = error instanceof FileConflictError || errorMessage.includes('already exists') diff --git a/apps/sim/app/api/v1/knowledge/search/route.test.ts b/apps/sim/app/api/v1/knowledge/search/route.test.ts index 8c9842ca0b..3c854e4e5b 100644 --- a/apps/sim/app/api/v1/knowledge/search/route.test.ts +++ b/apps/sim/app/api/v1/knowledge/search/route.test.ts @@ -6,7 +6,9 @@ * * @vitest-environment node */ + import { createMockRequest, knowledgeApiUtilsMock, knowledgeApiUtilsMockFns } from '@sim/testing' +import { getErrorMessage } from '@sim/utils/errors' import { beforeEach, describe, expect, it, vi } from 'vitest' const { @@ -47,7 +49,7 @@ vi.mock('@/app/api/v1/middleware', () => ({ vi.mock('@/app/api/v1/knowledge/utils', () => ({ handleError: (e: unknown) => - new Response(JSON.stringify({ error: e instanceof Error ? e.message : 'error' }), { + new Response(JSON.stringify({ error: getErrorMessage(e, 'error') }), { status: 500, }), })) diff --git a/apps/sim/app/api/v1/workflows/[id]/route.ts b/apps/sim/app/api/v1/workflows/[id]/route.ts index 76d7393870..7532772fb4 100644 --- a/apps/sim/app/api/v1/workflows/[id]/route.ts +++ b/apps/sim/app/api/v1/workflows/[id]/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { workflowBlocks } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { getActiveWorkflowRecord } from '@sim/workflow-authz' import { eq } from 'drizzle-orm' @@ -92,7 +93,7 @@ export const GET = withRouteHandler( return NextResponse.json(apiResponse.body, { headers: apiResponse.headers }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Workflow details fetch error`, { error: message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/v1/workflows/route.ts b/apps/sim/app/api/v1/workflows/route.ts index 707a6a3de4..4b1bed44ef 100644 --- a/apps/sim/app/api/v1/workflows/route.ts +++ b/apps/sim/app/api/v1/workflows/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, asc, eq, gt, isNull, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -172,7 +173,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { return NextResponse.json(response.body, { headers: response.headers }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Workflows fetch error`, { error: message }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/webhooks/agentmail/route.ts b/apps/sim/app/api/webhooks/agentmail/route.ts index 63cb660dbb..9c4333c4fe 100644 --- a/apps/sim/app/api/webhooks/agentmail/route.ts +++ b/apps/sim/app/api/webhooks/agentmail/route.ts @@ -8,6 +8,7 @@ import { workspace, } from '@sim/db' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { tasks } from '@trigger.dev/sdk' import { and, eq, gt, isNotNull, ne, sql } from 'drizzle-orm' @@ -214,7 +215,7 @@ export const POST = withRouteHandler(async (req: Request) => { executeInboxTask(taskId).catch((err) => { logger.error('Local inbox task execution failed', { taskId, - error: err instanceof Error ? err.message : 'Unknown error', + error: getErrorMessage(err, 'Unknown error'), }) }) } @@ -223,7 +224,7 @@ export const POST = withRouteHandler(async (req: Request) => { executeInboxTask(taskId).catch((err) => { logger.error('Local inbox task execution failed', { taskId, - error: err instanceof Error ? err.message : 'Unknown error', + error: getErrorMessage(err, 'Unknown error'), }) }) } @@ -231,7 +232,7 @@ export const POST = withRouteHandler(async (req: Request) => { return NextResponse.json({ ok: true }) } catch (error) { logger.error('AgentMail webhook error', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts b/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts index 2d4312b54b..4f4098953e 100644 --- a/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts +++ b/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { cleanupExpiredIdempotencyKeys, getIdempotencyKeyStats } from '@/lib/core/idempotency' @@ -56,7 +57,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => { { success: false, message: 'Idempotency cleanup failed', - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), requestId, }, { status: 500 } diff --git a/apps/sim/app/api/webhooks/poll/[provider]/route.ts b/apps/sim/app/api/webhooks/poll/[provider]/route.ts index 84b0e7c0e0..3c8415ea34 100644 --- a/apps/sim/app/api/webhooks/poll/[provider]/route.ts +++ b/apps/sim/app/api/webhooks/poll/[provider]/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { type NextRequest, NextResponse } from 'next/server' import { webhookPollingContract } from '@/lib/api/contracts/webhooks' @@ -75,7 +76,7 @@ export const GET = withRouteHandler( { success: false, message: `${providerLabel} polling failed`, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), requestId, }, { status: 500 } diff --git a/apps/sim/app/api/webhooks/route.ts b/apps/sim/app/api/webhooks/route.ts index b53255d9e1..4740909c2f 100644 --- a/apps/sim/app/api/webhooks/route.ts +++ b/apps/sim/app/api/webhooks/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { permissions, webhook, workflow, workflowDeploymentVersion } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId, generateShortId } from '@sim/utils/id' import { assertWorkflowMutable, @@ -487,7 +488,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { error: `Failed to configure ${provider} webhook`, - details: err instanceof Error ? err.message : 'Unknown error', + details: getErrorMessage(err, 'Unknown error'), }, { status: 500 } ) @@ -542,7 +543,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { error: 'Failed to create external webhook subscription', - details: err instanceof Error ? err.message : 'Unknown error', + details: getErrorMessage(err, 'Unknown error'), }, { status: 500 } ) @@ -669,7 +670,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { return NextResponse.json( { error: `Failed to configure ${provider} webhook`, - details: err instanceof Error ? err.message : 'Unknown error', + details: getErrorMessage(err, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/workflows/[id]/autolayout/route.ts b/apps/sim/app/api/workflows/[id]/autolayout/route.ts index a46714cd4c..18d1864dae 100644 --- a/apps/sim/app/api/workflows/[id]/autolayout/route.ts +++ b/apps/sim/app/api/workflows/[id]/autolayout/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { assertWorkflowMutable, authorizeWorkflowByWorkspacePermission, @@ -157,7 +158,7 @@ export const POST = withRouteHandler( return NextResponse.json( { error: 'Autolayout failed', - details: error instanceof Error ? error.message : 'Unknown error', + details: getErrorMessage(error, 'Unknown error'), }, { status: 500 } ) diff --git a/apps/sim/app/api/workflows/[id]/deploy/route.ts b/apps/sim/app/api/workflows/[id]/deploy/route.ts index 1ea8721a82..23d574efc4 100644 --- a/apps/sim/app/api/workflows/[id]/deploy/route.ts +++ b/apps/sim/app/api/workflows/[id]/deploy/route.ts @@ -1,5 +1,6 @@ import { db, workflow } from '@sim/db' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { assertWorkflowMutable, WorkflowLockedError } from '@sim/workflow-authz' import { eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' @@ -136,7 +137,7 @@ export const POST = withRouteHandler( if (error instanceof WorkflowLockedError) { return createErrorResponse(error.message, error.status) } - const message = error instanceof Error ? error.message : 'Failed to deploy workflow' + const message = getErrorMessage(error, 'Failed to deploy workflow') logger.error(`[${requestId}] Error deploying workflow: ${id}`, { error }) return createErrorResponse(message, 500) } @@ -195,8 +196,7 @@ export const PATCH = withRouteHandler( if (error instanceof WorkflowLockedError) { return createErrorResponse(error.message, error.status) } - const message = - error instanceof Error ? error.message : 'Failed to update deployment settings' + const message = getErrorMessage(error, 'Failed to update deployment settings') logger.error(`[${requestId}] Error updating deployment settings`, { error }) return createErrorResponse(message, 500) } @@ -247,7 +247,7 @@ export const DELETE = withRouteHandler( if (error instanceof WorkflowLockedError) { return createErrorResponse(error.message, error.status) } - const message = error instanceof Error ? error.message : 'Failed to undeploy workflow' + const message = getErrorMessage(error, 'Failed to undeploy workflow') logger.error(`[${requestId}] Error undeploying workflow: ${id}`, { error }) return createErrorResponse(message, 500) } diff --git a/apps/sim/app/api/workflows/[id]/execute/route.ts b/apps/sim/app/api/workflows/[id]/execute/route.ts index b2d80e8ddb..7140344463 100644 --- a/apps/sim/app/api/workflows/[id]/execute/route.ts +++ b/apps/sim/app/api/workflows/[id]/execute/route.ts @@ -1,7 +1,7 @@ import { db } from '@sim/db' import { workflow as workflowTable } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { generateId, isValidUuid } from '@sim/utils/id' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { eq } from 'drizzle-orm' @@ -671,7 +671,7 @@ async function handleExecutePost( await loggingSession.safeCompleteWithError({ error: { - message: `File processing failed: ${fileError instanceof Error ? fileError.message : 'Unable to process input files'}`, + message: `File processing failed: ${getErrorMessage(fileError, 'Unable to process input files')}`, stackTrace: fileError instanceof Error ? fileError.stack : undefined, }, traceSpans: [], @@ -679,7 +679,7 @@ async function handleExecutePost( return NextResponse.json( { - error: `File processing failed: ${fileError instanceof Error ? fileError.message : 'Unable to process input files'}`, + error: `File processing failed: ${getErrorMessage(fileError, 'Unable to process input files')}`, }, { status: 400 } ) @@ -814,7 +814,7 @@ async function handleExecutePost( return NextResponse.json(filteredResult) } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') reqLogger.error(`Non-SSE execution failed: ${errorMessage}`) @@ -1371,9 +1371,7 @@ async function handleExecutePost( const isTimeout = isTimeoutError(error) || timeoutController.isTimedOut() const errorMessage = isTimeout ? getTimeoutErrorMessage(error, timeoutController.timeoutMs) - : error instanceof Error - ? error.message - : 'Unknown error' + : getErrorMessage(error, 'Unknown error') reqLogger.error(`SSE execution failed: ${errorMessage}`, { isTimeout }) @@ -1433,7 +1431,7 @@ async function handleExecutePost( await eventWriter.close().catch((closeError) => { reqLogger.warn('Failed to close execution event writer after terminal publish', { executionId, - error: closeError instanceof Error ? closeError.message : String(closeError), + error: getErrorMessage(closeError), }) }) } else { diff --git a/apps/sim/app/api/workflows/[id]/form/status/route.ts b/apps/sim/app/api/workflows/[id]/form/status/route.ts index b2bc3fd8fd..cb77489a39 100644 --- a/apps/sim/app/api/workflows/[id]/form/status/route.ts +++ b/apps/sim/app/api/workflows/[id]/form/status/route.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { form } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { authorizeWorkflowByWorkspacePermission } from '@sim/workflow-authz' import { and, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' @@ -12,10 +13,6 @@ import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/ const logger = createLogger('FormStatusAPI') -function getErrorMessage(error: unknown, fallback: string): string { - return error instanceof Error ? error.message : fallback -} - export const GET = withRouteHandler( async (request: NextRequest, context: { params: Promise<{ id: string }> }) => { try { diff --git a/apps/sim/app/api/workflows/[id]/restore/route.ts b/apps/sim/app/api/workflows/[id]/restore/route.ts index 7d20bf3e3e..65a8fa9619 100644 --- a/apps/sim/app/api/workflows/[id]/restore/route.ts +++ b/apps/sim/app/api/workflows/[id]/restore/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { assertFolderMutable, FolderLockedError, WorkflowLockedError } from '@sim/workflow-authz' import { type NextRequest, NextResponse } from 'next/server' import { restoreWorkflowContract } from '@/lib/api/contracts/workflows' @@ -78,7 +79,7 @@ export const POST = withRouteHandler( logger.error(`[${requestId}] Error restoring workflow ${workflowId}`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Internal server error' }, + { error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/workflows/[id]/variables/route.ts b/apps/sim/app/api/workflows/[id]/variables/route.ts index 934f366f11..b2fd323324 100644 --- a/apps/sim/app/api/workflows/[id]/variables/route.ts +++ b/apps/sim/app/api/workflows/[id]/variables/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { assertWorkflowMutable, authorizeWorkflowByWorkspacePermission, @@ -168,7 +169,7 @@ export const GET = withRouteHandler( ) } catch (error) { logger.error(`[${requestId}] Workflow variables fetch error`, error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') return NextResponse.json({ error: errorMessage }, { status: 500 }) } } diff --git a/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts b/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts index 55b2059f5e..dab424729c 100644 --- a/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { apiKey } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, not } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { updateWorkspaceApiKeyContract } from '@/lib/api/contracts/api-keys' @@ -116,7 +117,7 @@ export const PUT = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] Workspace API key PUT error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to update workspace API key' }, + { error: getErrorMessage(error, 'Failed to update workspace API key') }, { status: 500 } ) } @@ -191,7 +192,7 @@ export const DELETE = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] Workspace API key DELETE error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to delete workspace API key' }, + { error: getErrorMessage(error, 'Failed to delete workspace API key') }, { status: 500 } ) } diff --git a/apps/sim/app/api/workspaces/[id]/api-keys/route.ts b/apps/sim/app/api/workspaces/[id]/api-keys/route.ts index 6242eb64a0..5706dd1699 100644 --- a/apps/sim/app/api/workspaces/[id]/api-keys/route.ts +++ b/apps/sim/app/api/workspaces/[id]/api-keys/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { apiKey } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, inArray } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { @@ -75,7 +76,7 @@ export const GET = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] Workspace API keys GET error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to load API keys' }, + { error: getErrorMessage(error, 'Failed to load API keys') }, { status: 500 } ) } @@ -136,7 +137,7 @@ export const POST = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] Workspace API key POST error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to create workspace API key' }, + { error: getErrorMessage(error, 'Failed to create workspace API key') }, { status: 500 } ) } @@ -207,7 +208,7 @@ export const DELETE = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] Workspace API key DELETE error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to delete workspace API keys' }, + { error: getErrorMessage(error, 'Failed to delete workspace API keys') }, { status: 500 } ) } diff --git a/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts b/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts index 89edbb98f2..fb2d52d7d3 100644 --- a/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts +++ b/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts @@ -2,6 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { workspaceBYOKKeys } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -98,7 +99,7 @@ export const GET = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] BYOK keys GET error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to load BYOK keys' }, + { error: getErrorMessage(error, 'Failed to load BYOK keys') }, { status: 500 } ) } @@ -233,7 +234,7 @@ export const POST = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] BYOK key POST error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to save BYOK key' }, + { error: getErrorMessage(error, 'Failed to save BYOK key') }, { status: 500 } ) } @@ -301,7 +302,7 @@ export const DELETE = withRouteHandler( } catch (error: unknown) { logger.error(`[${requestId}] BYOK key DELETE error`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to delete BYOK key' }, + { error: getErrorMessage(error, 'Failed to delete BYOK key') }, { status: 500 } ) } diff --git a/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts b/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts index d14969e131..d56e7ab644 100644 --- a/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workspaceFileParamsSchema } from '@/lib/api/contracts/workspace-files' import { getValidationErrorMessage } from '@/lib/api/server' @@ -66,7 +67,7 @@ export const POST = withRouteHandler( return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to generate download URL', + error: getErrorMessage(error, 'Failed to generate download URL'), }, { status: 500 } ) diff --git a/apps/sim/app/api/workspaces/[id]/files/[fileId]/restore/route.ts b/apps/sim/app/api/workspaces/[id]/files/[fileId]/restore/route.ts index fe22581038..0d41810c77 100644 --- a/apps/sim/app/api/workspaces/[id]/files/[fileId]/restore/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/[fileId]/restore/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workspaceFileParamsSchema } from '@/lib/api/contracts/workspace-files' import { getValidationErrorMessage } from '@/lib/api/server' @@ -55,7 +56,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error(`[${requestId}] Error restoring workspace file ${fileId}`, error) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Internal server error' }, + { error: getErrorMessage(error, 'Internal server error') }, { status: 500 } ) } diff --git a/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts b/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts index cba4dd8a72..1826988ea0 100644 --- a/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { renameWorkspaceFileContract, @@ -80,7 +81,7 @@ export const PATCH = withRouteHandler( return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to rename file', + error: getErrorMessage(error, 'Failed to rename file'), }, { status: 500 } ) @@ -158,7 +159,7 @@ export const DELETE = withRouteHandler( return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to delete file', + error: getErrorMessage(error, 'Failed to delete file'), }, { status: 500 } ) diff --git a/apps/sim/app/api/workspaces/[id]/files/presigned/route.ts b/apps/sim/app/api/workspaces/[id]/files/presigned/route.ts index 1227f93c50..abb83b3f6c 100644 --- a/apps/sim/app/api/workspaces/[id]/files/presigned/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/presigned/route.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { workspacePresignedUploadContract } from '@/lib/api/contracts/workspace-files' import { parseRequest } from '@/lib/api/server' @@ -52,7 +53,7 @@ export const POST = withRouteHandler( targetFolderId = await assertWorkspaceFileFolderTarget(workspaceId, folderId) } catch (error) { return NextResponse.json( - { error: error instanceof Error ? error.message : 'Invalid target folder' }, + { error: getErrorMessage(error, 'Invalid target folder') }, { status: 400 } ) } diff --git a/apps/sim/app/api/workspaces/[id]/files/register/route.ts b/apps/sim/app/api/workspaces/[id]/files/register/route.ts index 0b6d4876ab..fd29a6c9df 100644 --- a/apps/sim/app/api/workspaces/[id]/files/register/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/register/route.ts @@ -1,5 +1,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { registerWorkspaceFileContract } from '@/lib/api/contracts/workspace-files' import { parseRequest } from '@/lib/api/server' @@ -90,7 +91,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error('Failed to register workspace file:', error) - const errorMessage = error instanceof Error ? error.message : 'Failed to register file' + const errorMessage = getErrorMessage(error, 'Failed to register file') const isDuplicate = error instanceof FileConflictError || errorMessage.includes('already exists') const isMissing = errorMessage.includes('not found in storage') diff --git a/apps/sim/app/api/workspaces/[id]/files/route.ts b/apps/sim/app/api/workspaces/[id]/files/route.ts index d8d4c2e691..75caa8542e 100644 --- a/apps/sim/app/api/workspaces/[id]/files/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/route.ts @@ -1,5 +1,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { type NextRequest, NextResponse } from 'next/server' import { listWorkspaceFilesQuerySchema, @@ -78,7 +79,7 @@ export const GET = withRouteHandler( return NextResponse.json( { success: false, - error: error instanceof Error ? error.message : 'Failed to list files', + error: getErrorMessage(error, 'Failed to list files'), }, { status: 500 } ) @@ -183,7 +184,7 @@ export const POST = withRouteHandler( } catch (error) { logger.error(`[${requestId}] Error uploading workspace file:`, error) - const errorMessage = error instanceof Error ? error.message : 'Failed to upload file' + const errorMessage = getErrorMessage(error, 'Failed to upload file') const isDuplicate = error instanceof FileConflictError || errorMessage.includes('already exists') diff --git a/apps/sim/app/api/workspaces/[id]/inbox/route.ts b/apps/sim/app/api/workspaces/[id]/inbox/route.ts index 697dbdb26e..aa3a3ccc35 100644 --- a/apps/sim/app/api/workspaces/[id]/inbox/route.ts +++ b/apps/sim/app/api/workspaces/[id]/inbox/route.ts @@ -1,5 +1,6 @@ import { db, mothershipInboxTask, workspace } from '@sim/db' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { updateInboxConfigContract } from '@/lib/api/contracts/inbox' @@ -128,10 +129,10 @@ export const PATCH = withRouteHandler( } catch (error) { logger.error('Inbox config update failed', { workspaceId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to update inbox' }, + { error: getErrorMessage(error, 'Failed to update inbox') }, { status: 500 } ) } diff --git a/apps/sim/app/form/[identifier]/form.tsx b/apps/sim/app/form/[identifier]/form.tsx index cd18547338..f6264ddf0f 100644 --- a/apps/sim/app/form/[identifier]/form.tsx +++ b/apps/sim/app/form/[identifier]/form.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Loader } from '@/components/emcn' import { martianMono } from '@/app/_styles/fonts/martian-mono/martian-mono' import AuthBackground from '@/app/(auth)/components/auth-background' @@ -137,7 +138,7 @@ export default function Form({ identifier }: { identifier: string }) { } catch (err: unknown) { if (err instanceof Error && err.name === 'AbortError') return logger.error('Error fetching form config:', err) - setError(err instanceof Error ? err.message : 'Failed to load form') + setError(getErrorMessage(err, 'Failed to load form')) } finally { setIsLoading(false) } @@ -189,7 +190,7 @@ export default function Form({ identifier }: { identifier: string }) { setIsSubmitted(true) } catch (err: unknown) { logger.error('Error submitting form:', err) - setError(err instanceof Error ? err.message : 'Failed to submit form') + setError(getErrorMessage(err, 'Failed to submit form')) } finally { setIsSubmitting(false) } @@ -219,7 +220,7 @@ export default function Form({ identifier }: { identifier: string }) { await fetchFormConfig() } catch (err: unknown) { logger.error('Error authenticating:', err) - setError(err instanceof Error ? err.message : 'Invalid password') + setError(getErrorMessage(err, 'Invalid password')) setIsLoading(false) } }, diff --git a/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx b/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx index 8ad86d3222..67114faec4 100644 --- a/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx +++ b/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx @@ -39,7 +39,7 @@ export default async function ResumeExecutionPageWrapper({ return ( ) diff --git a/apps/sim/app/unsubscribe/unsubscribe.tsx b/apps/sim/app/unsubscribe/unsubscribe.tsx index 69dd17eb3a..3804267ea4 100644 --- a/apps/sim/app/unsubscribe/unsubscribe.tsx +++ b/apps/sim/app/unsubscribe/unsubscribe.tsx @@ -1,6 +1,7 @@ 'use client' import { Suspense, useEffect, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { useSearchParams } from 'next/navigation' import { Loader } from '@/components/emcn' import { requestJson } from '@/lib/api/client/request' @@ -34,7 +35,7 @@ function UnsubscribeContent() { setData(response) }) .catch((err: unknown) => { - const message = err instanceof Error ? err.message : 'Failed to validate unsubscribe link' + const message = getErrorMessage(err, 'Failed to validate unsubscribe link') setError(message) }) .finally(() => { @@ -80,7 +81,7 @@ function UnsubscribeContent() { } } } catch (err: unknown) { - const message = err instanceof Error ? err.message : 'Failed to process unsubscribe request' + const message = getErrorMessage(err, 'Failed to process unsubscribe request') setError(message) } finally { setProcessing(false) diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts index 2c922ca1d8..3ec0168918 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { useQueryClient } from '@tanstack/react-query' @@ -4692,7 +4692,7 @@ export function useChat( clearActiveTurn() setTransportIdle() } - setError(err instanceof Error ? err.message : 'Failed to stop the previous response') + setError(getErrorMessage(err, 'Failed to stop the previous response')) return false } } @@ -4862,7 +4862,7 @@ export function useChat( if (succeeded) return consumedByTranscript } - setError(err instanceof Error ? err.message : 'Failed to send message') + setError(getErrorMessage(err, 'Failed to send message')) if (gen !== undefined && streamGenRef.current === gen) { finalize({ error: true, @@ -5193,7 +5193,7 @@ export function useChat( pendingStopPromiseRef.current = null pendingStopModeRef.current = null } - setError(err instanceof Error ? err.message : 'Failed to stop the previous response') + setError(getErrorMessage(err, 'Failed to stop the previous response')) rejectStopOperation(err) throw err } @@ -5266,7 +5266,7 @@ export function useChat( pendingStopPromiseRef.current = null pendingStopModeRef.current = null } - setError(err instanceof Error ? err.message : 'Failed to stop the previous response') + setError(getErrorMessage(err, 'Failed to stop the previous response')) rejectStopOperation(err) throw err } @@ -5401,7 +5401,7 @@ export function useChat( if (activeChatId) { invalidateChatQueries() } - setError(err instanceof Error ? err.message : 'Failed to stop the previous response') + setError(getErrorMessage(err, 'Failed to stop the previous response')) rejectStopOperation(err) throw err } finally { diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx index b35018dba2..1652e6eb2d 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { format } from 'date-fns' import { AlertCircle, Pencil, Plus, Tag, X } from 'lucide-react' @@ -468,8 +469,7 @@ export function KnowledgeBase({ logger.error('Error retrying document:', err) updateDocument(docId, { processingStatus: 'failed', - processingError: - err instanceof Error ? err.message : 'Failed to retry document processing', + processingError: getErrorMessage(err, 'Failed to retry document processing'), }) }, } diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/rename-document-modal/rename-document-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/rename-document-modal/rename-document-modal.tsx index 74df75e4b2..e426cc48e8 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/rename-document-modal/rename-document-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/rename-document-modal/rename-document-modal.tsx @@ -2,6 +2,7 @@ import { useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Button, Input, @@ -73,7 +74,7 @@ export function RenameDocumentModal({ onOpenChange(false) } catch (err) { logger.error('Error renaming document:', err) - setError(err instanceof Error ? err.message : 'Failed to rename document') + setError(getErrorMessage(err, 'Failed to rename document')) } finally { setIsSubmitting(false) } diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx index e25bc03106..6ce5212967 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx @@ -3,6 +3,7 @@ import { memo, useEffect, useRef, useState } from 'react' import { zodResolver } from '@hookform/resolvers/zod' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { RotateCcw, X } from 'lucide-react' import { useParams } from 'next/navigation' import { useForm } from 'react-hook-form' @@ -365,7 +366,7 @@ export const CreateBaseModal = memo(function CreateBaseModal({ logger.error('Error creating knowledge base:', error) setSubmitStatus({ type: 'error', - message: error instanceof Error ? error.message : 'An unknown error occurred', + message: getErrorMessage(error, 'An unknown error occurred'), }) } } diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/edit-knowledge-base-modal/edit-knowledge-base-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/edit-knowledge-base-modal/edit-knowledge-base-modal.tsx index f79faf6d8c..13e4e849c9 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/edit-knowledge-base-modal/edit-knowledge-base-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/edit-knowledge-base-modal/edit-knowledge-base-modal.tsx @@ -3,6 +3,7 @@ import { memo, useEffect, useState } from 'react' import { zodResolver } from '@hookform/resolvers/zod' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { useForm } from 'react-hook-form' import { z } from 'zod' import { @@ -94,7 +95,7 @@ export const EditKnowledgeBaseModal = memo(function EditKnowledgeBaseModal({ onOpenChange(false) } catch (err) { logger.error('Error updating knowledge base:', err) - setError(err instanceof Error ? err.message : 'Failed to update knowledge base') + setError(getErrorMessage(err, 'Failed to update knowledge base')) } finally { setIsSubmitting(false) } diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload.ts b/apps/sim/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload.ts index 87ebc397b7..c9c2931bce 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload.ts +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload.ts @@ -1,5 +1,6 @@ import { useCallback, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { useQueryClient } from '@tanstack/react-query' import { @@ -91,9 +92,6 @@ class ProcessingError extends KnowledgeUploadError { } } -const getErrorMessage = (error: unknown): string => - error instanceof Error ? error.message : typeof error === 'string' ? error : 'Unknown error' - interface BatchPresignedFile { fileName: string contentType: string diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx index 92a184b60e..5faf30a587 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx @@ -1,4 +1,5 @@ import { memo, useEffect, useMemo, useRef, useState } from 'react' +import { generateShortId } from '@sim/utils/id' import { Button } from '@/components/emcn' import { cn } from '@/lib/core/utils/cn' import { formatDate, formatLatency } from '@/app/workspace/[workspaceId]/logs/utils' @@ -30,7 +31,7 @@ function LineChartComponent({ series?: LineChartMultiSeries[] }) { const containerRef = useRef(null) - const uniqueId = useRef(`chart-${Math.random().toString(36).substring(2, 9)}`).current + const uniqueId = useRef(`chart-${generateShortId(7)}`).current const [containerWidth, setContainerWidth] = useState(null) const width = containerWidth ?? 0 const height = 166 diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx index a59f6fd7ef..fc0f34931e 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/components/slack-channel-selector/slack-channel-selector.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Hash, Lock } from 'lucide-react' import { Combobox, type ComboboxOption } from '@/components/emcn' import { requestJson } from '@/lib/api/client/request' @@ -59,7 +60,7 @@ export function SlackChannelSelector({ ) } catch (err) { logger.error('Failed to fetch Slack channels', { error: err }) - setFetchError(err instanceof Error ? err.message : 'Failed to fetch channels') + setFetchError(getErrorMessage(err, 'Failed to fetch channels')) setChannels([]) } finally { setIsLoading(false) diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx index be1d4ea812..e8e5a9dea5 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx @@ -3,6 +3,7 @@ import type { ReactNode } from 'react' import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Plus, X } from 'lucide-react' import { Badge, @@ -506,7 +507,7 @@ export const NotificationSettings = memo(function NotificationSettings({ resetForm() setShowForm(false) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to save notification' + const message = getErrorMessage(error, 'Failed to save notification') setFormErrors({ general: message }) } } diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx index 2992257a05..4773c76a7a 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx @@ -2,6 +2,7 @@ import { useMemo, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Button, ButtonGroup, @@ -300,9 +301,7 @@ export function ScheduleModal({ open, onOpenChange, workspaceId, schedule }: Sch handleClose() } catch (error: unknown) { logger.error('Schedule submission failed:', { error }) - setSubmitError( - error instanceof Error ? error.message : 'Failed to save scheduled task. Please try again.' - ) + setSubmitError(getErrorMessage(error, 'Failed to save scheduled task. Please try again.')) } } diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx index 5b4cf3eb3f..f1be6c1b11 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/admin/admin.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useMemo, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { WrenchIcon } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -446,7 +447,7 @@ export function Admin() { {usersError && (

- {usersError instanceof Error ? usersError.message : 'Failed to fetch users'} + {getErrorMessage(usersError, 'Failed to fetch users')}

)} diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/components/create-api-key-modal/create-api-key-modal.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/components/create-api-key-modal/create-api-key-modal.tsx index 89bf444814..4f1cbeb6bc 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/components/create-api-key-modal/create-api-key-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/components/create-api-key-modal/create-api-key-modal.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Button, ButtonGroup, @@ -89,8 +90,7 @@ export function CreateApiKeyModal({ onKeyCreated?.(data.key) } catch (error: unknown) { logger.error('API key creation failed:', { error }) - const errorMessage = - error instanceof Error ? error.message : 'Failed to create Sim key. Please try again.' + const errorMessage = getErrorMessage(error, 'Failed to create Sim key. Please try again.') if (errorMessage.toLowerCase().includes('already exists')) { setCreateError(errorMessage) } else { diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx index cfdaf31c19..306b62850c 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx @@ -2,6 +2,7 @@ import { useMemo, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Eye, EyeOff, Search } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -196,7 +197,7 @@ export function BYOK() { setApiKeyInput('') setShowApiKey(false) } catch (err) { - const message = err instanceof Error ? err.message : 'Failed to save API key' + const message = getErrorMessage(err, 'Failed to save API key') setError(message) logger.error('Failed to save BYOK key', { error: err }) } diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/custom-tools.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/custom-tools.tsx index c52c9d39e3..01d6d027d9 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/custom-tools.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/custom-tools.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Plus, Search } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -119,7 +120,7 @@ export function CustomTools() { {error ? (

- {error instanceof Error ? error.message : 'Failed to load tools'} + {getErrorMessage(error, 'Failed to load tools')}

) : isLoading ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/inbox-settings-tab.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/inbox-settings-tab.tsx index 0136366864..d796cb7f30 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/inbox-settings-tab.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/inbox-settings-tab.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { Check, Clipboard, Pencil, Plus, Trash2 } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -62,7 +63,7 @@ export function InboxSettingsTab() { setIsEditAddressOpen(false) setNewUsername('') } catch (error) { - setEditAddressError(error instanceof Error ? error.message : 'Failed to update address') + setEditAddressError(getErrorMessage(error, 'Failed to update address')) } }, [workspaceId, newUsername]) @@ -79,7 +80,7 @@ export function InboxSettingsTab() { setNewSenderEmail('') setNewSenderLabel('') } catch (error) { - setAddSenderError(error instanceof Error ? error.message : 'Failed to add sender') + setAddSenderError(getErrorMessage(error, 'Failed to add sender')) } }, [workspaceId, newSenderEmail, newSenderLabel]) @@ -89,7 +90,7 @@ export function InboxSettingsTab() { try { await removeSender.mutateAsync({ workspaceId, senderId }) } catch (error) { - setRemoveSenderError(error instanceof Error ? error.message : 'Failed to remove sender') + setRemoveSenderError(getErrorMessage(error, 'Failed to remove sender')) } }, [workspaceId] diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/integrations-manager.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/integrations-manager.tsx index 6e32675aa4..ab794a1cda 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/integrations-manager.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/integrations-manager.tsx @@ -2,6 +2,7 @@ import { createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { AlertTriangle, Check, Clipboard, Plus, Search, Share2 } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -278,7 +279,7 @@ export function IntegrationsManager() { if (isDescriptionDirty) setSelectedDescriptionDraft((v) => v.trim()) } } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to save changes' + const message = getErrorMessage(error, 'Failed to save changes') setDetailsError(message) logger.error('Failed to save credential details', error) } @@ -455,7 +456,7 @@ export function IntegrationsManager() { callbackURL: window.location.href, }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to start OAuth connection' + const message = getErrorMessage(error, 'Failed to start OAuth connection') setCreateError(message) logger.error('Failed to connect OAuth service', error) } @@ -505,7 +506,7 @@ export function IntegrationsManager() { setShowDeleteConfirmDialog(false) setCredentialToDelete(null) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to disconnect integration' + const message = getErrorMessage(error, 'Failed to disconnect integration') setDeleteError(message) logger.error('Failed to disconnect integration', error) } @@ -530,7 +531,7 @@ export function IntegrationsManager() { }) } } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to share with workspace' + const message = getErrorMessage(error, 'Failed to share with workspace') setDetailsError(message) logger.error('Failed to share credential with workspace', error) } finally { @@ -576,7 +577,7 @@ export function IntegrationsManager() { callbackURL: window.location.href, }) } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to start reconnect' + const message = getErrorMessage(error, 'Failed to start reconnect') setDetailsError(message) logger.error('Failed to reconnect OAuth credential', error) } diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/service-account-form.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/service-account-form.tsx index 21b0b001d3..524e8b1600 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/service-account-form.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/integrations/service-account-form.tsx @@ -1,6 +1,7 @@ 'use client' import { createElement, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { Badge, Button, @@ -120,7 +121,7 @@ export function ServiceAccountForm({ }) onCreated() } catch (err: unknown) { - const message = err instanceof Error ? err.message : 'Failed to add service account' + const message = getErrorMessage(err, 'Failed to add service account') setError(message) } finally { setIsSubmitting(false) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx index 8ac6a35fd9..79209191d4 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Button, Input as EmcnInput, @@ -327,7 +328,7 @@ export function McpServerFormModal({ if (open && !prevOpen) { const data = initialData ?? DEFAULT_FORM_DATA setFormData(data) - setOriginalData(JSON.parse(JSON.stringify(data))) + setOriginalData(structuredClone(data)) setFormMode('form') setJsonInput('') setJsonError(null) @@ -520,7 +521,7 @@ export function McpServerFormModal({ onOpenChange(false) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to save server' + const message = getErrorMessage(error, 'Failed to save server') setSubmitError(message) logger.error('Failed to save MCP server:', error) } finally { @@ -571,7 +572,7 @@ export function McpServerFormModal({ onOpenChange(false) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to save server' + const message = getErrorMessage(error, 'Failed to save server') setSubmitError(message) logger.error('Failed to save MCP server from JSON:', error) } finally { diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx index a500a4d28d..a21da0f563 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { ChevronDown, Plus, Search } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -628,7 +629,7 @@ export function MCP({ initialServerId }: MCPProps) { {error ? (

- {error instanceof Error ? error.message : 'Failed to load MCP servers'} + {getErrorMessage(error, 'Failed to load MCP servers')}

) : serversLoading ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/secrets-manager.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/secrets-manager.tsx index 95529a6b53..39798d3e3c 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/secrets-manager.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/secrets-manager.tsx @@ -2,6 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateShortId } from '@sim/utils/id' import { useQueryClient } from '@tanstack/react-query' import { Check, Clipboard, Key, Search } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' @@ -217,7 +219,7 @@ function WorkspaceVariableRow({ onPendingKeyChange(e.target.value) }} onBlur={() => onRenameEnd(envKey, value)} - name={`workspace_env_key_${envKey}_${Math.random()}`} + name={`workspace_env_key_${envKey}_${generateShortId()}`} autoComplete='off' autoCapitalize='off' spellCheck='false' @@ -242,7 +244,7 @@ function WorkspaceVariableRow({ onBlur={() => { if (canEdit) setValueFocused(false) }} - name={`workspace_env_value_${envKey}_${Math.random()}`} + name={`workspace_env_value_${envKey}_${generateShortId()}`} autoComplete='off' autoCorrect='off' autoCapitalize='off' @@ -298,7 +300,7 @@ function NewWorkspaceVariableRow({ onChange={(e) => onUpdate(index, 'key', e.target.value)} onPaste={onPaste ? (e) => onPaste(e, index) : undefined} placeholder='API_KEY' - name={`new_workspace_key_${envVar.id || index}_${Math.random()}`} + name={`new_workspace_key_${envVar.id || index}_${generateShortId()}`} autoComplete='off' autoCapitalize='off' spellCheck='false' @@ -314,7 +316,7 @@ function NewWorkspaceVariableRow({ onPaste={onPaste ? (e) => onPaste(e, index) : undefined} placeholder='Enter value' type={valueFocused ? 'text' : 'password'} - name={`new_workspace_value_${envVar.id || index}_${Math.random()}`} + name={`new_workspace_value_${envVar.id || index}_${generateShortId()}`} autoComplete='off' autoCapitalize='off' spellCheck='false' @@ -596,8 +598,8 @@ export function SecretsManager() { })), createEmptyEnvVar(), ] - initialVarsRef.current = JSON.parse(JSON.stringify(initialVars)) - setEnvVars(JSON.parse(JSON.stringify(initialVars))) + initialVarsRef.current = structuredClone(initialVars) + setEnvVars(structuredClone(initialVars)) }, [personalEnvData]) useEffect(() => { @@ -782,7 +784,7 @@ export function SecretsManager() { }) } } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to save changes' + const message = getErrorMessage(error, 'Failed to save changes') setDetailsError(message) logger.error('Failed to save secret details', error) } @@ -954,7 +956,7 @@ export function SecretsManager() { } const resetToSaved = () => { - setEnvVars(JSON.parse(JSON.stringify(initialVarsRef.current))) + setEnvVars(structuredClone(initialVarsRef.current)) setWorkspaceVars({ ...initialWorkspaceVarsRef.current }) setNewWorkspaceRows([createEmptyEnvVar()]) setShowUnsavedChanges(false) @@ -1035,7 +1037,7 @@ export function SecretsManager() { if (firstFailure) throw firstFailure.reason initialWorkspaceVarsRef.current = { ...mergedWorkspaceVars } - initialVarsRef.current = JSON.parse(JSON.stringify(envVars.filter((v) => v.key && v.value))) + initialVarsRef.current = structuredClone(envVars.filter((v) => v.key && v.value)) setWorkspaceVars(mergedWorkspaceVars) setNewWorkspaceRows([createEmptyEnvVar()]) @@ -1088,7 +1090,7 @@ export function SecretsManager() { onChange={(e) => updateEnvVar(originalIndex, 'key', e.target.value)} onPaste={(e) => handlePaste(e, originalIndex)} placeholder='API_KEY' - name={`env_variable_name_${envVar.id || originalIndex}_${Math.random()}`} + name={`env_variable_name_${envVar.id || originalIndex}_${generateShortId()}`} autoComplete='off' autoCapitalize='off' spellCheck='false' @@ -1116,7 +1118,7 @@ export function SecretsManager() { onBlur={() => setFocusedValueIndex(null)} onPaste={(e) => handlePaste(e, originalIndex)} placeholder={isConflict ? 'Workspace override active' : 'Enter value'} - name={`env_variable_value_${envVar.id || originalIndex}_${Math.random()}`} + name={`env_variable_value_${envVar.id || originalIndex}_${generateShortId()}`} autoComplete='off' autoCapitalize='off' spellCheck='false' diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/skills/components/skill-import.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/skills/components/skill-import.tsx index c27379e67f..0efc4f2c46 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/skills/components/skill-import.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/skills/components/skill-import.tsx @@ -2,6 +2,7 @@ import type { ChangeEvent } from 'react' import { useCallback, useRef, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { Button, Input, Label, Loader, Textarea } from '@/components/emcn' import { Upload } from '@/components/emcn/icons' import { requestJson } from '@/lib/api/client/request' @@ -72,7 +73,7 @@ export function SkillImport({ onImport }: SkillImportProps) { setFileState('idle') onImport(parsed) } catch (err) { - const message = err instanceof Error ? err.message : 'Failed to process file' + const message = getErrorMessage(err, 'Failed to process file') setFileError(message) setFileState('error') } @@ -136,7 +137,7 @@ export function SkillImport({ onImport }: SkillImportProps) { setGithubState('idle') onImport(parsed) } catch (err) { - const message = err instanceof Error ? err.message : 'Failed to import from GitHub' + const message = getErrorMessage(err, 'Failed to import from GitHub') setGithubError(message) setGithubState('error') } diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/skills/skills.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/skills/skills.tsx index ebf9a344d2..788465b205 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/skills/skills.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/skills/skills.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Plus, Search } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -113,7 +114,7 @@ export function Skills() { {error ? (

- {error instanceof Error ? error.message : 'Failed to load skills'} + {getErrorMessage(error, 'Failed to load skills')}

) : isLoading ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/subscription/subscription.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/subscription/subscription.tsx index e55e686f66..7408dc0e84 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/subscription/subscription.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/subscription/subscription.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Info } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -503,7 +504,7 @@ export function Subscription() { ...(seats ? { seats } : {}), }) } catch (error) { - alert(error instanceof Error ? error.message : 'Unknown error occurred') + alert(getErrorMessage(error, 'Unknown error occurred')) } }, [handleUpgrade, isAnnual] @@ -735,9 +736,7 @@ export function Subscription() { handleSwitchInterval(isAnnual ? 'year' : 'month') .then(() => setManagePlanModalOpen(false)) .catch((e) => - alert( - e instanceof Error ? e.message : 'Failed to switch interval' - ) + alert(getErrorMessage(e, 'Failed to switch interval')) ) : () => doUpgrade('pro', PRO_TIER.credits) } @@ -772,7 +771,7 @@ export function Subscription() { : isOnMaxTier && wantsIntervalSwitch ? () => handleSwitchInterval(isAnnual ? 'year' : 'month').catch((e) => - alert(e instanceof Error ? e.message : 'Failed to switch interval') + alert(getErrorMessage(e, 'Failed to switch interval')) ) : subscription.isPaid ? async () => { @@ -786,7 +785,7 @@ export function Subscription() { }) await refetchSubscription() } catch (e) { - alert(e instanceof Error ? e.message : 'Failed to upgrade') + alert(getErrorMessage(e, 'Failed to upgrade')) } } : () => doUpgrade('pro', MAX_TIER.credits) @@ -862,7 +861,7 @@ export function Subscription() { await refetchSubscription() setManagePlanModalOpen(false) } catch (e) { - alert(e instanceof Error ? e.message : 'Failed to switch plan') + alert(getErrorMessage(e, 'Failed to switch plan')) } }} onUpgradeToCurrentTier={async () => { @@ -879,7 +878,7 @@ export function Subscription() { await refetchSubscription() setManagePlanModalOpen(false) } catch (e) { - alert(e instanceof Error ? e.message : 'Failed to migrate plan') + alert(getErrorMessage(e, 'Failed to migrate plan')) } }} onGetForTeam={() => { @@ -911,7 +910,7 @@ export function Subscription() { await betterAuthSubscription.cancel({ returnUrl, referenceId }) } catch (e) { logger.error('Failed to cancel subscription', { error: e }) - alert(e instanceof Error ? e.message : 'Failed to cancel subscription') + alert(getErrorMessage(e, 'Failed to cancel subscription')) } }} onRestore={async () => { @@ -933,7 +932,7 @@ export function Subscription() { setManagePlanModalOpen(false) } catch (e) { logger.error('Failed to restore subscription', { error: e }) - alert(e instanceof Error ? e.message : 'Failed to restore subscription') + alert(getErrorMessage(e, 'Failed to restore subscription')) } }} /> @@ -1306,7 +1305,7 @@ function ManagePlanModal({ try { await onSwitchInterval(targetInterval) } catch (e) { - setError(e instanceof Error ? e.message : 'Failed to switch interval') + setError(getErrorMessage(e, 'Failed to switch interval')) } finally { setIsSwitching(false) } diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx index 132226fe50..463793b2fd 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Check, Clipboard, Plus, Search, Server } from 'lucide-react' import { useParams } from 'next/navigation' import { @@ -1044,7 +1045,7 @@ export function WorkflowMcpServers() { {error ? (

- {error instanceof Error ? error.message : 'Failed to load MCP servers'} + {getErrorMessage(error, 'Failed to load MCP servers')}

) : isLoading ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/settings/hooks/use-profile-picture-upload.ts b/apps/sim/app/workspace/[workspaceId]/settings/hooks/use-profile-picture-upload.ts index e0143fffab..f00b2b7a21 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/hooks/use-profile-picture-upload.ts +++ b/apps/sim/app/workspace/[workspaceId]/settings/hooks/use-profile-picture-upload.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { uploadViaApiFallback } from '@/lib/uploads/client/api-fallback' import { DirectUploadError, runUploadStrategy } from '@/lib/uploads/client/direct-upload' @@ -114,8 +115,7 @@ export function useProfilePictureUpload({ setPreviewUrl(serverUrl) onUploadRef.current?.(serverUrl) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to upload profile picture' + const errorMessage = getErrorMessage(error, 'Failed to upload profile picture') onErrorRef.current?.(errorMessage) URL.revokeObjectURL(newPreviewUrl) previewRef.current = null diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx index b596beaa60..a3c217eb63 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { useParams } from 'next/navigation' import { Button, @@ -120,7 +121,7 @@ export function RowModal({ mode, isOpen, onClose, table, row, rowIds, onSuccess onSuccess() } catch (err) { logger.error(`Failed to ${mode} row:`, err) - setError(err instanceof Error ? err.message : `Failed to ${mode} row`) + setError(getErrorMessage(err, `Failed to ${mode} row`)) } } @@ -139,7 +140,7 @@ export function RowModal({ mode, isOpen, onClose, table, row, rowIds, onSuccess onSuccess() } catch (err) { logger.error('Failed to delete row(s):', err) - setError(err instanceof Error ? err.message : 'Failed to delete row(s)') + setError(getErrorMessage(err, 'Failed to delete row(s)')) } } diff --git a/apps/sim/app/workspace/[workspaceId]/tables/components/import-csv-dialog/import-csv-dialog.tsx b/apps/sim/app/workspace/[workspaceId]/tables/components/import-csv-dialog/import-csv-dialog.tsx index edc50bb4c0..b6f7e5beca 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/components/import-csv-dialog/import-csv-dialog.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/components/import-csv-dialog/import-csv-dialog.tsx @@ -2,6 +2,7 @@ import { useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Button, ButtonGroup, @@ -172,7 +173,7 @@ export function ImportCsvDialog({ }) setMapping(autoMapping) } catch (err) { - const message = err instanceof Error ? err.message : 'Failed to parse CSV' + const message = getErrorMessage(err, 'Failed to parse CSV') logger.error('CSV parse failed', err) setParseError(message) } finally { @@ -327,7 +328,7 @@ export function ImportCsvDialog({ }) onOpenChange(false) } catch (err) { - const message = err instanceof Error ? err.message : 'Failed to import CSV' + const message = getErrorMessage(err, 'Failed to import CSV') setSubmitError(summarizeImportError(message)) logger.error('CSV import into existing table failed', err) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/form/form.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/form/form.tsx index 8cc4eda2f7..5ae66012f3 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/form/form.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/form/form.tsx @@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Check, Eye, EyeOff } from 'lucide-react' import { ButtonGroup, @@ -269,7 +270,7 @@ export function FormDeploy({ onDeploymentComplete?.() } } catch (err: unknown) { - const message = err instanceof Error ? err.message : 'An error occurred' + const message = getErrorMessage(err, 'An error occurred') logger.error('Error deploying form:', err) if (message.toLowerCase().includes('identifier')) { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/api-info-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/api-info-modal.tsx index f075b9eeee..4046953ec5 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/api-info-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/api-info-modal.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { useParams } from 'next/navigation' import { Badge, @@ -195,7 +196,7 @@ export function ApiInfoModal({ open, onOpenChange, workflowId }: ApiInfoModalPro onOpenChange(false) } catch (err: unknown) { - const message = err instanceof Error ? err.message : 'Failed to update access settings' + const message = getErrorMessage(err, 'Failed to update access settings') setSaveError(message) } finally { setIsSaving(false) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-predeploy-checks.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-predeploy-checks.ts index ef4d941226..eb06c38706 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-predeploy-checks.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-predeploy-checks.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import type { Edge } from 'reactflow' import { validateWorkflowSchedules } from '@/lib/workflows/schedules/validation' import { Serializer } from '@/serializer' @@ -40,7 +41,7 @@ const requiredFieldsCheck: PreDeployCheck = ({ blocks, edges, loops, parallels } } catch (error) { return { passed: false, - error: error instanceof Error ? error.message : 'Workflow validation failed', + error: getErrorMessage(error, 'Workflow validation failed'), } } } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/combobox/combobox.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/combobox/combobox.tsx index 4451f01762..51a1b1b40d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/combobox/combobox.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/combobox/combobox.tsx @@ -1,4 +1,5 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { isEqual } from 'es-toolkit' import { useReactFlow } from 'reactflow' import { useStoreWithEqualityFn } from 'zustand/traditional' @@ -134,7 +135,7 @@ export const ComboBox = memo(function ComboBox({ const options = await fetchOptions(blockId) setFetchedOptions(options) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch options' + const errorMessage = getErrorMessage(error, 'Failed to fetch options') setFetchError(errorMessage) setFetchedOptions([]) } finally { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx index 0534816dc2..681da1f49e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx @@ -1,4 +1,5 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { isEqual } from 'es-toolkit' import { useStoreWithEqualityFn } from 'zustand/traditional' @@ -160,7 +161,7 @@ export const Dropdown = memo(function Dropdown({ const options = await fetchOptions(blockId) setFetchedOptions(options) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Failed to fetch options' + const errorMessage = getErrorMessage(error, 'Failed to fetch options') setFetchError(errorMessage) setFetchedOptions([]) } finally { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx index 0cc64dfbfa..efd15dbb0e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx @@ -2,6 +2,9 @@ import { useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateShortId } from '@sim/utils/id' +import { randomFloat } from '@sim/utils/random' import { useQueryClient } from '@tanstack/react-query' import { X } from 'lucide-react' import { useParams } from 'next/navigation' @@ -293,7 +296,7 @@ export function FileUpload({ } const uploading = validFiles.map((file) => ({ - id: `upload-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, + id: `upload-${Date.now()}-${generateShortId(7)}`, name: file.name, size: file.size, })) @@ -308,7 +311,7 @@ export function FileUpload({ progressInterval = setInterval(() => { setUploadProgress((prev) => { - const newProgress = prev + Math.random() * 10 + const newProgress = prev + randomFloat() * 10 return newProgress > 90 ? 90 : newProgress }) }, 200) @@ -334,7 +337,7 @@ export function FileUpload({ }) } catch (error) { logger.error(`Error uploading ${file.name}:`, error) - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') uploadErrors.push(`${file.name}: ${errorMessage}`) setUploadError(errorMessage) } @@ -397,10 +400,7 @@ export function FileUpload({ useWorkflowStore.getState().triggerUpdate() } } catch (error) { - logger.error( - error instanceof Error ? error.message : 'Failed to upload file(s)', - activeWorkflowId - ) + logger.error(getErrorMessage(error, 'Failed to upload file(s)'), activeWorkflowId) } finally { if (progressInterval) { clearInterval(progressInterval) @@ -488,10 +488,7 @@ export function FileUpload({ useWorkflowStore.getState().triggerUpdate() } catch (error) { - logger.error( - error instanceof Error ? error.message : 'Failed to remove file', - activeWorkflowId - ) + logger.error(getErrorMessage(error, 'Failed to remove file'), activeWorkflowId) } finally { setDeletingFiles((prev) => { const updated = { ...prev } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table-selector/table-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table-selector/table-selector.tsx index 61a662d1d6..0eaed94834 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table-selector/table-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/table-selector/table-selector.tsx @@ -59,7 +59,7 @@ export function TableSelector({ [isPreview, disabled, setStoreValue] ) - const errorMessage = error instanceof Error ? error.message : error ? String(error) : undefined + const errorMessage = error?.message return ( ( newValue === null ? null : typeof newValue === 'object' - ? JSON.parse(JSON.stringify(newValue)) + ? structuredClone(newValue) : newValue // If streaming, hold value locally and do not update global store to avoid render-phase updates @@ -180,7 +180,7 @@ export function useSubBlockValue( } // Emit immediately; the client queue coalesces same-key ops and the server debounces - emitValue(valueCopy) + emitValue(valueCopy as T) if (triggerWorkflowUpdate) { useWorkflowStore.getState().triggerUpdate() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index 357d3a030a..979af8479a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -1,5 +1,6 @@ import { memo, useCallback, useEffect, useMemo, useRef } from 'react' import { createLogger } from '@sim/logger' +import { truncate } from '@sim/utils/string' import { isEqual } from 'es-toolkit' import { useParams } from 'next/navigation' import { Handle, type NodeProps, Position, useUpdateNodeInternals } from 'reactflow' @@ -264,7 +265,7 @@ export const getDisplayValue = (value: unknown): string => { const firstMessage = parsedValue[0] if (!firstMessage?.content || firstMessage.content.trim() === '') return '-' const content = firstMessage.content.trim() - return content.length > 50 ? `${content.slice(0, 50)}...` : content + return truncate(content, 50) } if (isVariableAssignmentsArray(parsedValue)) { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-auto-layout.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-auto-layout.ts index 6dfc29760e..48f1f6ba9f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-auto-layout.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-auto-layout.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { useReactFlow } from 'reactflow' import type { AutoLayoutOptions } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils' import { applyAutoLayoutAndUpdateStore as applyAutoLayoutStandalone } from '@/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils' @@ -60,7 +61,7 @@ export function useAutoLayout(workflowId: string | null, options?: CanvasViewpor logger.error('Auto layout error:', error) return { success: false, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), } } }, [applyAutoLayoutAndUpdateStore, fitViewToBounds]) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts index 604d0edd9c..47211da7b1 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts @@ -1,8 +1,8 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' -import { generateId } from '@sim/utils/id' +import { generateId, generateShortId } from '@sim/utils/id' import { useQueryClient } from '@tanstack/react-query' import { useParams } from 'next/navigation' import { useShallow } from 'zustand/react/shallow' @@ -71,7 +71,7 @@ const logger = createLogger('useWorkflowExecution') const activeReconnections = new Set() function isReconnectNonRetryable(error: unknown): boolean { - const message = error instanceof Error ? error.message : '' + const message = getErrorMessage(error, '') return ( message.includes('Execution events pruned before requested event id') || (isExecutionStreamHttpError(error) && @@ -518,7 +518,7 @@ export function useWorkflowExecution() { presignedEndpoint, }) uploadedFiles.push({ - id: `file_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, + id: `file_${Date.now()}_${generateShortId(7)}`, name: fileData.file.name, url: result.path, size: fileData.file.size, @@ -558,9 +558,7 @@ export function useWorkflowExecution() { } const uploadResult = await response.json() const processUploadResult = (r: any) => ({ - id: - r.id || - `file_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, + id: r.id || `file_${Date.now()}_${generateShortId(7)}`, name: r.name, url: r.url, size: r.size, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils.ts index 7f3aca63af..564eb79640 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type { Edge } from 'reactflow' import { ApiClientError } from '@/lib/api/client/errors' import { requestJson } from '@/lib/api/client/request' @@ -184,11 +185,11 @@ export async function applyAutoLayoutAndUpdateStore( return { success: false, - error: `Failed to save positions to database: ${saveError instanceof Error ? saveError.message : 'Unknown error'}`, + error: `Failed to save positions to database: ${getErrorMessage(saveError, 'Unknown error')}`, } } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown store update error' + const errorMessage = getErrorMessage(error, 'Unknown store update error') logger.error('Failed to update store with auto layout:', { workflowId, error: errorMessage }) return { diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-logo-upload.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-logo-upload.ts index 23d41f3ac2..0d589d2499 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-logo-upload.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-workspace-logo-upload.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { uploadViaApiFallback } from '@/lib/uploads/client/api-fallback' import { DirectUploadError, runUploadStrategy } from '@/lib/uploads/client/direct-upload' @@ -109,8 +110,7 @@ export function useWorkspaceLogoUpload({ setPreviewUrl(serverUrl) onUploadRef.current?.(serverUrl) } catch (error) { - const errorMessage = - error instanceof Error ? error.message : 'Failed to upload workspace logo' + const errorMessage = getErrorMessage(error, 'Failed to upload workspace logo') onErrorRef.current?.(errorMessage) URL.revokeObjectURL(newPreviewUrl) previewRef.current = null diff --git a/apps/sim/background/workspace-notification-delivery.ts b/apps/sim/background/workspace-notification-delivery.ts index fdaae3257d..1baaa3b20b 100644 --- a/apps/sim/background/workspace-notification-delivery.ts +++ b/apps/sim/background/workspace-notification-delivery.ts @@ -9,6 +9,8 @@ import { hmacSha256Hex } from '@sim/security/hmac' import { toError } from '@sim/utils/errors' import { formatDuration } from '@sim/utils/formatting' import { generateId } from '@sim/utils/id' +import { randomFloat } from '@sim/utils/random' +import { truncate } from '@sim/utils/string' import { getActiveWorkflowContext } from '@sim/workflow-authz' import { task } from '@trigger.dev/sdk' import { and, eq, isNull, lte, or, sql } from 'drizzle-orm' @@ -34,7 +36,7 @@ const logger = createLogger('WorkspaceNotificationDelivery') const MAX_ATTEMPTS = 5 const RETRY_DELAYS = [5 * 1000, 15 * 1000, 60 * 1000, 3 * 60 * 1000, 10 * 60 * 1000] function getRetryDelayWithJitter(baseDelay: number): number { - const jitter = Math.random() * 0.1 * baseDelay + const jitter = randomFloat() * 0.1 * baseDelay return Math.floor(baseDelay + jitter) } @@ -396,7 +398,7 @@ async function deliverSlack( if (payload.data.finalOutput) { const outputStr = JSON.stringify(payload.data.finalOutput, null, 2) - const truncated = outputStr.length > 2900 ? `${outputStr.slice(0, 2900)}...` : outputStr + const truncated = truncate(outputStr, 2900) blocks.push({ type: 'section', text: { diff --git a/apps/sim/blocks/blocks/apollo.ts b/apps/sim/blocks/blocks/apollo.ts index 3ee33e9035..3a0aa50344 100644 --- a/apps/sim/blocks/blocks/apollo.ts +++ b/apps/sim/blocks/blocks/apollo.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { ApolloIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' @@ -1041,7 +1042,7 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes parseJsonField(field) } } catch (error) { - const message = error instanceof Error ? error.message : String(error) + const message = getErrorMessage(error) throw new Error(`Invalid JSON input: ${message}`) } diff --git a/apps/sim/blocks/blocks/mongodb.ts b/apps/sim/blocks/blocks/mongodb.ts index cf1398f295..1882bc120f 100644 --- a/apps/sim/blocks/blocks/mongodb.ts +++ b/apps/sim/blocks/blocks/mongodb.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { MongoDBIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' @@ -844,7 +845,7 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow try { parsedDocuments = JSON.parse(documents) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid JSON documents format: ${errorMsg}. Please check your JSON syntax.` ) diff --git a/apps/sim/blocks/blocks/mysql.ts b/apps/sim/blocks/blocks/mysql.ts index 9e47e817e7..4ace662d83 100644 --- a/apps/sim/blocks/blocks/mysql.ts +++ b/apps/sim/blocks/blocks/mysql.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { MySQLIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' @@ -325,7 +326,7 @@ Return ONLY the SQL query - no explanations, no markdown, no extra text.`, try { parsedData = JSON.parse(data) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error(`Invalid JSON data format: ${errorMsg}. Please check your JSON syntax.`) } } else if (data && typeof data === 'object') { diff --git a/apps/sim/blocks/blocks/neo4j.ts b/apps/sim/blocks/blocks/neo4j.ts index df502c237c..5931f36cc9 100644 --- a/apps/sim/blocks/blocks/neo4j.ts +++ b/apps/sim/blocks/blocks/neo4j.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { Neo4jIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' @@ -628,8 +629,7 @@ Return ONLY valid JSON.`, try { parsedParameters = JSON.parse(trimmed) } catch (parseError) { - const errorMsg = - parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid JSON parameters format: ${errorMsg}. Please check your JSON syntax.` ) diff --git a/apps/sim/blocks/blocks/postgresql.ts b/apps/sim/blocks/blocks/postgresql.ts index 53bf028ed6..a447dc5472 100644 --- a/apps/sim/blocks/blocks/postgresql.ts +++ b/apps/sim/blocks/blocks/postgresql.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { PostgresIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' @@ -335,7 +336,7 @@ Return ONLY the SQL query - no explanations, no markdown, no extra text.`, try { parsedData = JSON.parse(data) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error(`Invalid JSON data format: ${errorMsg}. Please check your JSON syntax.`) } } else if (data && typeof data === 'object') { diff --git a/apps/sim/blocks/blocks/rds.ts b/apps/sim/blocks/blocks/rds.ts index 71ac6822e8..a4e9341ed8 100644 --- a/apps/sim/blocks/blocks/rds.ts +++ b/apps/sim/blocks/blocks/rds.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { RDSIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' @@ -404,8 +405,7 @@ Return ONLY the JSON object.`, try { return JSON.parse(value) } catch (parseError) { - const errorMsg = - parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error(`Invalid JSON in ${fieldName}: ${errorMsg}`) } } diff --git a/apps/sim/blocks/blocks/sqs.ts b/apps/sim/blocks/blocks/sqs.ts index cc65c3f038..702e44f405 100644 --- a/apps/sim/blocks/blocks/sqs.ts +++ b/apps/sim/blocks/blocks/sqs.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { SQSIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { IntegrationType } from '@/blocks/types' @@ -100,8 +101,7 @@ export const SQSBlock: BlockConfig = { try { return JSON.parse(value) } catch (parseError) { - const errorMsg = - parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error(`Invalid JSON in ${fieldName}: ${errorMsg}`) } } diff --git a/apps/sim/blocks/blocks/supabase.ts b/apps/sim/blocks/blocks/supabase.ts index 2ef6b973ad..764a4d18e7 100644 --- a/apps/sim/blocks/blocks/supabase.ts +++ b/apps/sim/blocks/blocks/supabase.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { SupabaseIcon } from '@/components/icons' import { AuthMode, type BlockConfig, IntegrationType } from '@/blocks/types' import { normalizeFileInput } from '@/blocks/utils' @@ -1007,7 +1008,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e parsedData = JSON.parse(data) } catch (parseError) { // Provide more detailed error information - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid JSON data format: ${errorMsg}. Please check your JSON syntax (e.g., strings must be quoted like "value").` ) @@ -1028,7 +1029,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e try { parsedQueryEmbedding = JSON.parse(queryEmbedding) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid query embedding format: ${errorMsg}. Please provide a valid array of numbers like [0.1, 0.2, 0.3].` ) @@ -1043,7 +1044,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e try { parsedRpcParams = JSON.parse(rpcParams) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid RPC params format: ${errorMsg}. Please provide a valid JSON object.` ) @@ -1058,7 +1059,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e try { parsedPaths = JSON.parse(paths) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid paths format: ${errorMsg}. Please provide a valid JSON array like ["path1", "path2"].` ) @@ -1073,7 +1074,7 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e try { parsedAllowedMimeTypes = JSON.parse(allowedMimeTypes) } catch (parseError) { - const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown JSON error' + const errorMsg = getErrorMessage(parseError, 'Unknown JSON error') throw new Error( `Invalid allowedMimeTypes format: ${errorMsg}. Please provide a valid JSON array.` ) diff --git a/apps/sim/connectors/airtable/airtable.ts b/apps/sim/connectors/airtable/airtable.ts index 1ef2153615..48f808cc68 100644 --- a/apps/sim/connectors/airtable/airtable.ts +++ b/apps/sim/connectors/airtable/airtable.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { AirtableIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -308,7 +308,7 @@ export const airtableConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/asana/asana.ts b/apps/sim/connectors/asana/asana.ts index ed20942aeb..0418fc5487 100644 --- a/apps/sim/connectors/asana/asana.ts +++ b/apps/sim/connectors/asana/asana.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { AsanaIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -362,7 +362,7 @@ export const asanaConnector: ConnectorConfig = { ) return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/discord/discord.ts b/apps/sim/connectors/discord/discord.ts index bf589ff9c0..5167987169 100644 --- a/apps/sim/connectors/discord/discord.ts +++ b/apps/sim/connectors/discord/discord.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { DiscordIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -299,7 +299,7 @@ export const discordConnector: ConnectorConfig = { ) return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') if (message.includes('401') || message.includes('403')) { return { valid: false, error: 'Invalid bot token or missing permissions for this channel' } } diff --git a/apps/sim/connectors/dropbox/dropbox.ts b/apps/sim/connectors/dropbox/dropbox.ts index 7584783a7b..c612eb5aca 100644 --- a/apps/sim/connectors/dropbox/dropbox.ts +++ b/apps/sim/connectors/dropbox/dropbox.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { DropboxIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -293,7 +293,7 @@ export const dropboxConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/fireflies/fireflies.ts b/apps/sim/connectors/fireflies/fireflies.ts index 3334a347ca..0251fb4a69 100644 --- a/apps/sim/connectors/fireflies/fireflies.ts +++ b/apps/sim/connectors/fireflies/fireflies.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { FirefliesIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -337,7 +337,7 @@ export const firefliesConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/github/github.ts b/apps/sim/connectors/github/github.ts index 24040aa641..50d7fa3425 100644 --- a/apps/sim/connectors/github/github.ts +++ b/apps/sim/connectors/github/github.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { GithubIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -396,7 +396,7 @@ export const githubConnector: ConnectorConfig = { } catch (error) { return { valid: false, - error: error instanceof Error ? error.message : 'Invalid repository format', + error: getErrorMessage(error, 'Invalid repository format'), } } @@ -436,7 +436,7 @@ export const githubConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/gmail/gmail.ts b/apps/sim/connectors/gmail/gmail.ts index a8590e3846..bff9a86e91 100644 --- a/apps/sim/connectors/gmail/gmail.ts +++ b/apps/sim/connectors/gmail/gmail.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { GmailIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -594,7 +594,7 @@ export const gmailConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/google-calendar/google-calendar.ts b/apps/sim/connectors/google-calendar/google-calendar.ts index d107cb0569..83b1e1fd7b 100644 --- a/apps/sim/connectors/google-calendar/google-calendar.ts +++ b/apps/sim/connectors/google-calendar/google-calendar.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { GoogleCalendarIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -427,7 +428,7 @@ export const googleCalendarConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/google-drive/google-drive.ts b/apps/sim/connectors/google-drive/google-drive.ts index f132edbc3c..4371d110ff 100644 --- a/apps/sim/connectors/google-drive/google-drive.ts +++ b/apps/sim/connectors/google-drive/google-drive.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { GoogleDriveIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -387,7 +387,7 @@ export const googleDriveConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/google-sheets/google-sheets.ts b/apps/sim/connectors/google-sheets/google-sheets.ts index d69f91e0da..38ac6b175a 100644 --- a/apps/sim/connectors/google-sheets/google-sheets.ts +++ b/apps/sim/connectors/google-sheets/google-sheets.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { GoogleSheetsIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -380,7 +380,7 @@ export const googleSheetsConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/hubspot/hubspot.ts b/apps/sim/connectors/hubspot/hubspot.ts index 2395784e80..04e4db3a67 100644 --- a/apps/sim/connectors/hubspot/hubspot.ts +++ b/apps/sim/connectors/hubspot/hubspot.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { HubspotIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -366,7 +367,7 @@ export const hubspotConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/intercom/intercom.ts b/apps/sim/connectors/intercom/intercom.ts index 9d703f9505..7b7362dc3d 100644 --- a/apps/sim/connectors/intercom/intercom.ts +++ b/apps/sim/connectors/intercom/intercom.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { z } from 'zod' import { IntercomIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' @@ -511,7 +511,7 @@ export const intercomConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/linear/linear.ts b/apps/sim/connectors/linear/linear.ts index f25aafe815..6aa181fe65 100644 --- a/apps/sim/connectors/linear/linear.ts +++ b/apps/sim/connectors/linear/linear.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { LinearIcon } from '@/components/icons' import type { RetryOptions } from '@/lib/knowledge/documents/utils' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' @@ -402,7 +402,7 @@ export const linearConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/microsoft-teams/microsoft-teams.ts b/apps/sim/connectors/microsoft-teams/microsoft-teams.ts index 97729aca3c..bfb857085c 100644 --- a/apps/sim/connectors/microsoft-teams/microsoft-teams.ts +++ b/apps/sim/connectors/microsoft-teams/microsoft-teams.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { MicrosoftTeamsIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -398,7 +398,7 @@ export const microsoftTeamsConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/notion/notion.ts b/apps/sim/connectors/notion/notion.ts index 40efc9eada..49da592e51 100644 --- a/apps/sim/connectors/notion/notion.ts +++ b/apps/sim/connectors/notion/notion.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { NotionIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -377,7 +377,7 @@ export const notionConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/onedrive/onedrive.ts b/apps/sim/connectors/onedrive/onedrive.ts index b8ab17c936..9ee72da855 100644 --- a/apps/sim/connectors/onedrive/onedrive.ts +++ b/apps/sim/connectors/onedrive/onedrive.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { MicrosoftOneDriveIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -372,7 +372,7 @@ export const onedriveConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/outlook/outlook.ts b/apps/sim/connectors/outlook/outlook.ts index b0081e5af4..81d750b7f2 100644 --- a/apps/sim/connectors/outlook/outlook.ts +++ b/apps/sim/connectors/outlook/outlook.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { OutlookIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -628,7 +628,7 @@ export const outlookConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/reddit/reddit.ts b/apps/sim/connectors/reddit/reddit.ts index b63015df86..ccb5c53dbe 100644 --- a/apps/sim/connectors/reddit/reddit.ts +++ b/apps/sim/connectors/reddit/reddit.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { RedditIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -450,7 +450,7 @@ export const redditConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') if (message.includes('404') || message.includes('403')) { return { valid: false, diff --git a/apps/sim/connectors/sharepoint/sharepoint.ts b/apps/sim/connectors/sharepoint/sharepoint.ts index 8ac32dc575..ac310ef90d 100644 --- a/apps/sim/connectors/sharepoint/sharepoint.ts +++ b/apps/sim/connectors/sharepoint/sharepoint.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { MicrosoftSharepointIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -534,7 +534,7 @@ export const sharepointConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/webflow/webflow.ts b/apps/sim/connectors/webflow/webflow.ts index bfa3936aed..638e3e7e42 100644 --- a/apps/sim/connectors/webflow/webflow.ts +++ b/apps/sim/connectors/webflow/webflow.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { WebflowIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -342,7 +342,7 @@ export const webflowConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/connectors/wordpress/wordpress.ts b/apps/sim/connectors/wordpress/wordpress.ts index 272ff16a91..2abe45c926 100644 --- a/apps/sim/connectors/wordpress/wordpress.ts +++ b/apps/sim/connectors/wordpress/wordpress.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { WordpressIcon } from '@/components/icons' import { fetchWithRetry, VALIDATE_RETRY_OPTIONS } from '@/lib/knowledge/documents/utils' import type { ConnectorConfig, ExternalDocument, ExternalDocumentList } from '@/connectors/types' @@ -276,7 +276,7 @@ export const wordpressConnector: ConnectorConfig = { return { valid: true } } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to validate configuration' + const message = getErrorMessage(error, 'Failed to validate configuration') return { valid: false, error: message } } }, diff --git a/apps/sim/ee/sso/components/sso-settings.tsx b/apps/sim/ee/sso/components/sso-settings.tsx index 94eef2f471..c7f0bf975b 100644 --- a/apps/sim/ee/sso/components/sso-settings.tsx +++ b/apps/sim/ee/sso/components/sso-settings.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Check, ChevronDown, Clipboard, Eye, EyeOff } from 'lucide-react' import { Button, @@ -312,7 +313,7 @@ export function SSO() { setIsEditing(false) setShowAdvanced(false) } catch (err) { - const message = err instanceof Error ? err.message : 'Unknown error occurred' + const message = getErrorMessage(err, 'Unknown error occurred') toast.error(message) logger.error('Failed to configure SSO provider', { error: err }) } diff --git a/apps/sim/executor/execution/engine.test.ts b/apps/sim/executor/execution/engine.test.ts index f0539ebf4f..b77b2f4f8f 100644 --- a/apps/sim/executor/execution/engine.test.ts +++ b/apps/sim/executor/execution/engine.test.ts @@ -1,6 +1,7 @@ /** * @vitest-environment node */ +import { sleep } from '@sim/utils/helpers' import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from 'vitest' vi.mock('@/lib/execution/cancellation', () => ({ @@ -102,7 +103,7 @@ function createMockNodeOrchestrator(executeDelay = 0): MockNodeOrchestrator { executeNode: vi.fn().mockImplementation(async () => { mock.executionCount++ if (executeDelay > 0) { - await new Promise((resolve) => setTimeout(resolve, executeDelay)) + await sleep(executeDelay) } return { nodeId: 'test', output: {}, isFinalOutput: false } }), @@ -620,10 +621,10 @@ describe('ExecutionEngine', () => { executeNode: vi.fn().mockImplementation(async (_ctx: ExecutionContext, nodeId: string) => { executedNodes.push(nodeId) if (nodeId === 'parallel0') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) throw new Error('Parallel branch failed') } - await new Promise((resolve) => setTimeout(resolve, 2)) + await sleep(2) return { nodeId, output: {}, isFinalOutput: false } }), handleNodeCompletion: vi.fn(), @@ -655,15 +656,15 @@ describe('ExecutionEngine', () => { executionCount: 0, executeNode: vi.fn().mockImplementation(async (_ctx: ExecutionContext, nodeId: string) => { if (nodeId === 'parallel0') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) throw new Error('First error') } if (nodeId === 'parallel1') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) throw new Error('Second error') } if (nodeId === 'parallel2') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) throw new Error('Third error') } return { nodeId, output: {}, isFinalOutput: false } @@ -696,11 +697,11 @@ describe('ExecutionEngine', () => { executionCount: 0, executeNode: vi.fn().mockImplementation(async (_ctx: ExecutionContext, nodeId: string) => { if (nodeId === 'fast-error') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) throw new Error('Fast error') } if (nodeId === 'slow') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) slowNodeCompleted = true return { nodeId, output: {}, isFinalOutput: false } } @@ -1089,7 +1090,7 @@ describe('ExecutionEngine', () => { } } if (nodeId === 'slow-work') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) return { nodeId, output: { slow: true }, isFinalOutput: false } } return { nodeId, output: {}, isFinalOutput: true } @@ -1208,7 +1209,7 @@ describe('ExecutionEngine', () => { } } if (nodeId === 'other') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) return { nodeId, output: { other: true }, isFinalOutput: true } } return { nodeId, output: {}, isFinalOutput: false } @@ -1250,7 +1251,7 @@ describe('ExecutionEngine', () => { } } if (nodeId === 'error-node') { - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) throw new Error('Parallel branch failed') } return { nodeId, output: {}, isFinalOutput: false } diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index e2f329c206..a96d2a611a 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { buildNextCallChain, validateCallChain } from '@/lib/execution/call-chain' import { snapshotService } from '@/lib/logs/execution/snapshot/service' @@ -259,7 +260,7 @@ export class WorkflowBlockHandler implements BlockHandler { * Parses nested error messages to extract workflow chain and root error. */ private buildNestedWorkflowErrorMessage(childWorkflowName: string, error: unknown): string { - const originalError = error instanceof Error ? error.message : 'Unknown error' + const originalError = getErrorMessage(error, 'Unknown error') // Extract any nested workflow names from the error message const { chain, rootError } = this.parseNestedWorkflowError(originalError) diff --git a/apps/sim/executor/utils/json.ts b/apps/sim/executor/utils/json.ts index ceeea98a33..8890e7cbb8 100644 --- a/apps/sim/executor/utils/json.ts +++ b/apps/sim/executor/utils/json.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { EVALUATOR } from '@/executor/constants' const logger = createLogger('JSONUtils') @@ -19,7 +20,7 @@ export function parseJSONOrThrow(value: string): any { try { return JSON.parse(value.trim()) } catch (error) { - throw new Error(`Invalid JSON: ${error instanceof Error ? error.message : 'Parse error'}`) + throw new Error(`Invalid JSON: ${getErrorMessage(error, 'Parse error')}`) } } diff --git a/apps/sim/hooks/queries/mcp.ts b/apps/sim/hooks/queries/mcp.ts index c32accb2d4..5601952d2b 100644 --- a/apps/sim/hooks/queries/mcp.ts +++ b/apps/sim/hooks/queries/mcp.ts @@ -1,5 +1,6 @@ import { useEffect } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { ApiClientError } from '@/lib/api/client/errors' import { requestJson } from '@/lib/api/client/request' @@ -436,7 +437,7 @@ export function useMcpServerTest() { logger.info(`MCP server test ${result.success ? 'passed' : 'failed'}:`, variables.name) }, onError: (error) => { - logger.error('MCP server test failed:', error instanceof Error ? error.message : error) + logger.error('MCP server test failed:', getErrorMessage(error)) }, }) @@ -447,8 +448,7 @@ export function useMcpServerTest() { ? ({ success: false, message: 'Connection failed', - error: - mutation.error instanceof Error ? mutation.error.message : 'Unknown error occurred', + error: getErrorMessage(mutation.error, 'Unknown error occurred'), } as McpServerTestResult) : null), isTestingConnection: mutation.isPending, diff --git a/apps/sim/hooks/queries/providers.ts b/apps/sim/hooks/queries/providers.ts index 63786c71e5..c7eaabf503 100644 --- a/apps/sim/hooks/queries/providers.ts +++ b/apps/sim/hooks/queries/providers.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { useQuery } from '@tanstack/react-query' import { requestJson } from '@/lib/api/client/request' import { @@ -35,7 +36,7 @@ async function fetchProviderModels( } } catch (error) { logger.warn(`Failed to fetch ${provider} models`, { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) throw error } diff --git a/apps/sim/hooks/use-execution-stream.ts b/apps/sim/hooks/use-execution-stream.ts index c07fd2a3bf..24a6e0cad4 100644 --- a/apps/sim/hooks/use-execution-stream.ts +++ b/apps/sim/hooks/use-execution-stream.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type { BlockChildWorkflowStartedData, BlockCompletedData, @@ -158,7 +159,7 @@ export async function processSSEStream( eventType: event.type, eventId: event.eventId, }) - const message = error instanceof Error ? error.message : String(error) + const message = getErrorMessage(error) throw new SSEEventHandlerError( message, event.type, diff --git a/apps/sim/instrumentation-client.ts b/apps/sim/instrumentation-client.ts index fb6cf002ec..4164f5e225 100644 --- a/apps/sim/instrumentation-client.ts +++ b/apps/sim/instrumentation-client.ts @@ -2,6 +2,7 @@ * Sim Telemetry - Client-side Instrumentation */ +import { randomFloat } from '@sim/utils/random' import { env } from './lib/core/config/env' import { sanitizeEventData } from './lib/core/security/redaction' @@ -117,7 +118,7 @@ if (typeof window !== 'undefined') { } if (telemetryEnabled) { - const shouldTrackVitals = Math.random() < 0.1 + const shouldTrackVitals = randomFloat() < 0.1 if (shouldTrackVitals) { window.addEventListener( diff --git a/apps/sim/lib/api/contracts/tools/databases/shared.ts b/apps/sim/lib/api/contracts/tools/databases/shared.ts index 5b4c3f6a4f..de85eadd6a 100644 --- a/apps/sim/lib/api/contracts/tools/databases/shared.ts +++ b/apps/sim/lib/api/contracts/tools/databases/shared.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { z } from 'zod' export const sslModeSchema = z.enum(['disabled', 'required', 'preferred']).default('preferred') @@ -22,7 +23,7 @@ const jsonObjectStringSchema = (message: string, includeReceivedValue = false) = throw new Error(message) } - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') throw new Error(`${message}: ${errorMessage}. Received: ${str.substring(0, 100)}...`) } }) diff --git a/apps/sim/lib/billing/client/upgrade.ts b/apps/sim/lib/billing/client/upgrade.ts index 91b834e8fa..bbd0b5f8fe 100644 --- a/apps/sim/lib/billing/client/upgrade.ts +++ b/apps/sim/lib/billing/client/upgrade.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { useQueryClient } from '@tanstack/react-query' import { ApiClientError } from '@/lib/api/client/errors' import { requestJson } from '@/lib/api/client/request' @@ -103,7 +104,7 @@ export function useSubscriptionUpgrade() { } catch (error) { logger.warn('Failed to set organization as active, proceeding with upgrade', { organizationId: referenceId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } } else if (orgsData.isMemberOfAnyOrg) { @@ -202,7 +203,7 @@ export function useSubscriptionUpgrade() { } throw new Error( - `Failed to upgrade subscription: ${error instanceof Error ? error.message : 'Unknown error'}` + `Failed to upgrade subscription: ${getErrorMessage(error, 'Unknown error')}` ) } }, diff --git a/apps/sim/lib/billing/credits/purchase.ts b/apps/sim/lib/billing/credits/purchase.ts index 496f3b0cbb..e8dc21cd6f 100644 --- a/apps/sim/lib/billing/credits/purchase.ts +++ b/apps/sim/lib/billing/credits/purchase.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { organization, userStats } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { getPlanPricing } from '@/lib/billing/core/billing' import { isOrganizationOwnerOrAdmin } from '@/lib/billing/core/organization' @@ -226,7 +227,7 @@ export async function purchaseCredits(params: PurchaseCreditsParams): Promise( [TraceAttr.DbSystem]: 'postgresql', [TraceAttr.DbOperation]: op, [TraceAttr.DbSqlTable]: table, - ...Object.fromEntries(Object.entries(attrs).filter(([, v]) => v !== undefined)), + ...filterUndefined(attrs), }, }) try { diff --git a/apps/sim/lib/copilot/chat/post.ts b/apps/sim/lib/copilot/chat/post.ts index 521ee6d066..9eaa77b43d 100644 --- a/apps/sim/lib/copilot/chat/post.ts +++ b/apps/sim/lib/copilot/chat/post.ts @@ -2,6 +2,7 @@ import { type Context as OtelContext, context as otelContextApi } from '@opentel import { db } from '@sim/db' import { copilotChats, permissions } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' @@ -450,7 +451,7 @@ function buildOnComplete(params: { } catch (error) { logger.error(`[${requestId}] Failed to persist chat messages`, { chatId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } } @@ -482,7 +483,7 @@ function buildOnError(params: { } catch (error) { logger.error(`[${requestId}] Failed to finalize errored chat stream`, { chatId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } } @@ -815,7 +816,7 @@ export async function handleUnifiedChatPost(req: NextRequest) { const userPermissionPromise = workspaceId ? getUserEntityPermissions(authenticatedUserId, 'workspace', workspaceId).catch((error) => { logger.warn('Failed to load user permissions', { - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), workspaceId, }) return null @@ -1019,13 +1020,13 @@ export async function handleUnifiedChatPost(req: NextRequest) { } logger.error(`[${requestId}] Error handling unified chat request`, { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) return NextResponse.json( { - error: error instanceof Error ? error.message : 'Internal server error', + error: getErrorMessage(error, 'Internal server error'), }, { status: 500 } ) diff --git a/apps/sim/lib/copilot/request/go/stream.ts b/apps/sim/lib/copilot/request/go/stream.ts index c9d87b3ecc..45539d7402 100644 --- a/apps/sim/lib/copilot/request/go/stream.ts +++ b/apps/sim/lib/copilot/request/go/stream.ts @@ -1,5 +1,6 @@ import { type Context, SpanStatusCode } from '@opentelemetry/api' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { ORCHESTRATION_TIMEOUT_MS } from '@/lib/copilot/constants' import { type MothershipStreamV1EventType, @@ -334,7 +335,7 @@ export async function runStreamLoop( } catch (error) { logger.warn('Failed to forward stream event', { type: streamEvent.type, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) } @@ -451,7 +452,7 @@ export async function runStreamLoop( } catch (error) { logger.warn('Failed to read abort marker at body close', { streamId: context.messageId, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) } diff --git a/apps/sim/lib/copilot/request/handlers/handlers.test.ts b/apps/sim/lib/copilot/request/handlers/handlers.test.ts index 7fe971055a..40af3110c6 100644 --- a/apps/sim/lib/copilot/request/handlers/handlers.test.ts +++ b/apps/sim/lib/copilot/request/handlers/handlers.test.ts @@ -2,6 +2,7 @@ * @vitest-environment node */ +import { sleep } from '@sim/utils/helpers' import { beforeEach, describe, expect, it, vi } from 'vitest' import { TraceCollector } from '@/lib/copilot/request/trace' @@ -119,7 +120,7 @@ describe('sse-handlers tool lifecycle', () => { // tool_call fires execution without awaiting (fire-and-forget for parallel execution), // so we flush pending microtasks before asserting - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).toHaveBeenCalledTimes(1) expect(onEvent).toHaveBeenCalledWith( @@ -172,7 +173,7 @@ describe('sse-handlers tool lifecycle', () => { { onEvent, interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) const updated = context.toolCalls.get('tool-phase-label') expect(updated?.displayTitle).toBe('Workspace') @@ -208,7 +209,7 @@ describe('sse-handlers tool lifecycle', () => { { onEvent, interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(completeAsyncToolCall).toHaveBeenCalledWith( expect.objectContaining({ @@ -255,7 +256,7 @@ describe('sse-handlers tool lifecycle', () => { { interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).toHaveBeenCalledTimes(1) expect(context.contentBlocks).toEqual([]) @@ -310,7 +311,7 @@ describe('sse-handlers tool lifecycle', () => { { interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).toHaveBeenCalledWith( 'create_workflow', @@ -401,7 +402,7 @@ describe('sse-handlers tool lifecycle', () => { { interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(context.subAgentToolCalls['parent-1']?.[0]?.id).toBe('sub-tool-scope-1') }) @@ -422,7 +423,7 @@ describe('sse-handlers tool lifecycle', () => { } await sseHandlers.tool(event as StreamEvent, context, execContext, { interactive: false }) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) await sseHandlers.tool(event as StreamEvent, context, execContext, { interactive: false }) expect(executeTool).toHaveBeenCalledTimes(1) @@ -465,7 +466,7 @@ describe('sse-handlers tool lifecycle', () => { userStopController.abort() abortController.abort() - await new Promise((resolve) => setTimeout(resolve, 10)) + await sleep(10) const updated = context.toolCalls.get('tool-cancel') expect(updated?.status).toBe(MothershipStreamV1ToolOutcome.cancelled) @@ -495,7 +496,7 @@ describe('sse-handlers tool lifecycle', () => { } await sseHandlers.tool(event as StreamEvent, context, execContext, { interactive: false }) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) const firstPromise = context.pendingToolPromises.get('tool-inflight') expect(firstPromise).toBeDefined() @@ -506,7 +507,7 @@ describe('sse-handlers tool lifecycle', () => { expect(context.pendingToolPromises.get('tool-inflight')).toBe(firstPromise) resolveTool?.({ success: true, output: { ok: true } }) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(context.pendingToolPromises.has('tool-inflight')).toBe(false) }) @@ -532,7 +533,7 @@ describe('sse-handlers tool lifecycle', () => { { onEvent: vi.fn(), interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).toHaveBeenCalledTimes(1) expect(context.toolCalls.get('tool-upsert-fail')?.status).toBe( @@ -586,7 +587,7 @@ describe('sse-handlers tool lifecycle', () => { ) resolveUpsert?.(null) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).not.toHaveBeenCalled() expect(context.toolCalls.get('tool-race')?.status).toBe(MothershipStreamV1ToolOutcome.success) @@ -631,7 +632,7 @@ describe('sse-handlers tool lifecycle', () => { { onEvent, interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).not.toHaveBeenCalled() expect(context.toolCalls.get('tool-early-result')?.status).toBe( @@ -712,7 +713,7 @@ describe('sse-handlers tool lifecycle', () => { { interactive: false, timeout: 1000 } ) - await new Promise((resolve) => setTimeout(resolve, 0)) + await sleep(0) expect(executeTool).toHaveBeenCalledWith('gmail_read', { maxResults: 10 }, expect.any(Object)) expect(context.toolCalls.get('tool-dynamic-sim')?.status).toBe( diff --git a/apps/sim/lib/copilot/request/handlers/tool.ts b/apps/sim/lib/copilot/request/handlers/tool.ts index 802bcec329..1056ad0bee 100644 --- a/apps/sim/lib/copilot/request/handlers/tool.ts +++ b/apps/sim/lib/copilot/request/handlers/tool.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { upsertAsyncToolCall } from '@/lib/copilot/async-runs/repository' import { STREAM_TIMEOUT_MS } from '@/lib/copilot/constants' import { @@ -98,7 +98,7 @@ export async function prePersistClientExecutableToolCall( logger.warn('Failed to pre-persist async tool row before forwarding call frame', { toolCallId: data.toolCallId, toolName: data.toolName, - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) }) } diff --git a/apps/sim/lib/copilot/request/lifecycle/headless.ts b/apps/sim/lib/copilot/request/lifecycle/headless.ts index 381e91a395..0ae8fc75f5 100644 --- a/apps/sim/lib/copilot/request/lifecycle/headless.ts +++ b/apps/sim/lib/copilot/request/lifecycle/headless.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import type { RequestTraceV1Outcome as RequestTraceOutcome } from '@/lib/copilot/generated/request-trace-v1' import { @@ -97,7 +98,7 @@ export async function runHeadlessCopilotLifecycle( logger.warn('Failed to report headless trace', { simRequestId, chatId: result?.chatId ?? options.chatId, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) } } diff --git a/apps/sim/lib/copilot/request/lifecycle/start.ts b/apps/sim/lib/copilot/request/lifecycle/start.ts index 23628a0584..595efe15f3 100644 --- a/apps/sim/lib/copilot/request/lifecycle/start.ts +++ b/apps/sim/lib/copilot/request/lifecycle/start.ts @@ -2,6 +2,7 @@ import { type Context, context as otelContextApi } from '@opentelemetry/api' import { db } from '@sim/db' import { copilotChats } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { eq } from 'drizzle-orm' import { createRunSegment } from '@/lib/copilot/async-runs/repository' import { @@ -203,7 +204,7 @@ export function createSSEStream(params: StreamingOrchestrationParams): ReadableS requestContext: { requestId }, }).catch((error) => { logger.warn(`[${requestId}] Failed to create copilot run segment`, { - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) }) } @@ -291,11 +292,11 @@ export function createSSEStream(params: StreamingOrchestrationParams): ReadableS const wasCancelled = abortController.signal.aborted || publisher.clientDisconnected outcome = wasCancelled ? RequestTraceV1Outcome.cancelled : RequestTraceV1Outcome.error if (outcome === RequestTraceV1Outcome.cancelled) { - cancelReason = recordCancelled(error instanceof Error ? error.message : String(error)) + cancelReason = recordCancelled(getErrorMessage(error)) } if (publisher.clientDisconnected) { logger.info(`[${requestId}] Stream errored after client disconnect`, { - error: error instanceof Error ? error.message : 'Stream error', + error: getErrorMessage(error, 'Stream error'), }) } // Demote to warn when the throw came from a user-initiated @@ -327,7 +328,7 @@ export function createSSEStream(params: StreamingOrchestrationParams): ReadableS await publisher.close() } catch (error) { logger.warn(`[${requestId}] Failed to flush stream persistence during close`, { - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) } unregisterActiveStream(streamId) @@ -356,7 +357,7 @@ export function createSSEStream(params: StreamingOrchestrationParams): ReadableS }) reportTrace(trace, otelContext).catch((err) => { logger.warn(`[${requestId}] Failed to report trace`, { - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) }) rootOutcome = outcome @@ -399,7 +400,7 @@ export function createSSEStream(params: StreamingOrchestrationParams): ReadableS activeOtelRoot.finish(rootOutcome, rootError, cancelReason) } catch (finishError) { logger.error(`[${requestId}] activeOtelRoot.finish threw; force-ending root span`, { - error: finishError instanceof Error ? finishError.message : String(finishError), + error: getErrorMessage(finishError), }) try { activeOtelRoot.span.end() diff --git a/apps/sim/lib/copilot/request/session/contract.ts b/apps/sim/lib/copilot/request/session/contract.ts index 2d116e87ce..d084690186 100644 --- a/apps/sim/lib/copilot/request/session/contract.ts +++ b/apps/sim/lib/copilot/request/session/contract.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import type { MothershipStreamV1EventEnvelope, MothershipStreamV1StreamRef, @@ -462,7 +463,7 @@ export function parsePersistedStreamEventEnvelopeJson(raw: string): ParseStreamE try { parsed = JSON.parse(raw) } catch (error) { - const rawMessage = error instanceof Error ? error.message : 'Invalid JSON' + const rawMessage = getErrorMessage(error, 'Invalid JSON') return { ok: false, reason: 'invalid_json', diff --git a/apps/sim/lib/copilot/request/session/recovery.ts b/apps/sim/lib/copilot/request/session/recovery.ts index 8bad72847a..ab7e2e236d 100644 --- a/apps/sim/lib/copilot/request/session/recovery.ts +++ b/apps/sim/lib/copilot/request/session/recovery.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { MothershipStreamV1CompletionStatus, MothershipStreamV1EventType, @@ -116,7 +117,7 @@ async function resolveReplayGapRequestId( logger.warn('Failed to resolve request ID for replay gap', { streamId, latestSeq, - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) return '' } diff --git a/apps/sim/lib/copilot/tools/handlers/management/manage-custom-tool.ts b/apps/sim/lib/copilot/tools/handlers/management/manage-custom-tool.ts index e683403736..9387e71f93 100644 --- a/apps/sim/lib/copilot/tools/handlers/management/manage-custom-tool.ts +++ b/apps/sim/lib/copilot/tools/handlers/management/manage-custom-tool.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type { ExecutionContext, ToolCallResult } from '@/lib/copilot/request/types' import { deleteCustomTool, @@ -212,7 +212,7 @@ export async function executeManageCustomTool( ) return { success: false, - error: error instanceof Error ? error.message : 'Failed to manage custom tool', + error: getErrorMessage(error, 'Failed to manage custom tool'), } } } diff --git a/apps/sim/lib/copilot/tools/handlers/management/manage-mcp-tool.ts b/apps/sim/lib/copilot/tools/handlers/management/manage-mcp-tool.ts index 2543aed56a..a6b571c044 100644 --- a/apps/sim/lib/copilot/tools/handlers/management/manage-mcp-tool.ts +++ b/apps/sim/lib/copilot/tools/handlers/management/manage-mcp-tool.ts @@ -1,7 +1,7 @@ import { db } from '@sim/db' import { mcpServers } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { and, eq, isNull } from 'drizzle-orm' import type { ExecutionContext, ToolCallResult } from '@/lib/copilot/request/types' import { @@ -199,7 +199,7 @@ export async function executeManageMcpTool( ) return { success: false, - error: error instanceof Error ? error.message : 'Failed to manage MCP server', + error: getErrorMessage(error, 'Failed to manage MCP server'), } } } diff --git a/apps/sim/lib/copilot/tools/handlers/management/manage-skill.ts b/apps/sim/lib/copilot/tools/handlers/management/manage-skill.ts index 74a91b73c5..3320288f5f 100644 --- a/apps/sim/lib/copilot/tools/handlers/management/manage-skill.ts +++ b/apps/sim/lib/copilot/tools/handlers/management/manage-skill.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type { ExecutionContext, ToolCallResult } from '@/lib/copilot/request/types' import { deleteSkill, listSkills, upsertSkills } from '@/lib/workflows/skills/operations' @@ -168,7 +168,7 @@ export async function executeManageSkill( ) return { success: false, - error: error instanceof Error ? error.message : 'Failed to manage skill', + error: getErrorMessage(error, 'Failed to manage skill'), } } } diff --git a/apps/sim/lib/copilot/tools/handlers/materialize-file.ts b/apps/sim/lib/copilot/tools/handlers/materialize-file.ts index d58989cb56..7aa2b88c72 100644 --- a/apps/sim/lib/copilot/tools/handlers/materialize-file.ts +++ b/apps/sim/lib/copilot/tools/handlers/materialize-file.ts @@ -2,7 +2,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { db } from '@sim/db' import { workflow, workspaceFiles } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq, isNull } from 'drizzle-orm' import type { ExecutionContext, ToolCallResult } from '@/lib/copilot/request/types' @@ -225,7 +225,7 @@ export async function executeMaterializeFile( }) failed.push({ fileName, - error: err instanceof Error ? err.message : 'Failed to materialize file', + error: getErrorMessage(err, 'Failed to materialize file'), }) } } diff --git a/apps/sim/lib/copilot/tools/handlers/vfs.ts b/apps/sim/lib/copilot/tools/handlers/vfs.ts index a96cd5d3bf..ab87b264aa 100644 --- a/apps/sim/lib/copilot/tools/handlers/vfs.ts +++ b/apps/sim/lib/copilot/tools/handlers/vfs.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { TOOL_RESULT_MAX_INLINE_CHARS } from '@/lib/copilot/constants' import type { ExecutionContext, ToolCallResult } from '@/lib/copilot/request/types' import { getOrMaterializeVFS } from '@/lib/copilot/vfs' @@ -77,7 +77,7 @@ export async function executeVfsGrep( path: params.path, error: toError(err).message, }) - return { success: false, error: err instanceof Error ? err.message : 'vfs_grep failed' } + return { success: false, error: getErrorMessage(err, 'vfs_grep failed') } } } @@ -112,7 +112,7 @@ export async function executeVfsGlob( pattern, error: toError(err).message, }) - return { success: false, error: err instanceof Error ? err.message : 'vfs_glob failed' } + return { success: false, error: getErrorMessage(err, 'vfs_glob failed') } } } @@ -267,7 +267,7 @@ export async function executeVfsRead( path, error: toError(err).message, }) - return { success: false, error: err instanceof Error ? err.message : 'vfs_read failed' } + return { success: false, error: getErrorMessage(err, 'vfs_read failed') } } } @@ -295,6 +295,6 @@ async function executeVfsList( path, error: toError(err).message, }) - return { success: false, error: err instanceof Error ? err.message : 'vfs_list failed' } + return { success: false, error: getErrorMessage(err, 'vfs_list failed') } } } diff --git a/apps/sim/lib/copilot/tools/server/files/download-to-workspace-file.ts b/apps/sim/lib/copilot/tools/server/files/download-to-workspace-file.ts index 0573a9bc36..c9b8a5e4b6 100644 --- a/apps/sim/lib/copilot/tools/server/files/download-to-workspace-file.ts +++ b/apps/sim/lib/copilot/tools/server/files/download-to-workspace-file.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { z } from 'zod' import { DownloadToWorkspaceFile } from '@/lib/copilot/generated/tool-catalog-v1' import { ensureWorkspaceAccess } from '@/lib/copilot/tools/handlers/access' @@ -196,7 +197,7 @@ export const downloadToWorkspaceFileServerTool: BaseServerTool< downloadUrl: uploaded.url, } } catch (error) { - const msg = error instanceof Error ? error.message : 'Unknown error' + const msg = getErrorMessage(error, 'Unknown error') logger.error('Failed to download file to workspace', { url: params.url, error: msg, diff --git a/apps/sim/lib/copilot/tools/server/files/edit-content.ts b/apps/sim/lib/copilot/tools/server/files/edit-content.ts index 22d9539ba8..b668338120 100644 --- a/apps/sim/lib/copilot/tools/server/files/edit-content.ts +++ b/apps/sim/lib/copilot/tools/server/files/edit-content.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { assertServerToolNotAborted, type BaseServerTool, @@ -243,7 +243,7 @@ export const editContentServerTool: BaseServerTool 100 ? '...' : ''}"`, + message: `Patch failed: search string not found in file "${fileRecord.name}". Search: "${truncate(search, 100)}"`, } } if ( @@ -543,7 +544,7 @@ export const workspaceFileServerTool: BaseServerTool 500 ? `${tool.code.slice(0, 500)}...` : tool.code, + codePreview: truncate(tool.code, 500), }, null, 2 @@ -729,7 +730,7 @@ export function serializeSkill(s: { id: s.id, name: s.name, description: s.description, - contentPreview: s.content.length > 500 ? `${s.content.slice(0, 500)}...` : s.content, + contentPreview: truncate(s.content, 500), createdAt: s.createdAt.toISOString(), }, null, diff --git a/apps/sim/lib/core/config/redis.ts b/apps/sim/lib/core/config/redis.ts index c6e904ec7b..ae303b4d0a 100644 --- a/apps/sim/lib/core/config/redis.ts +++ b/apps/sim/lib/core/config/redis.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' +import { randomFloat } from '@sim/utils/random' import Redis from 'ioredis' import { env } from '@/lib/core/config/env' @@ -102,7 +103,7 @@ export function getRedisClient(): Redis | null { return 30000 } const base = Math.min(1000 * 2 ** (times - 1), 10000) - const jitter = Math.random() * base * 0.3 + const jitter = randomFloat() * base * 0.3 const delay = Math.round(base + jitter) logger.warn('Redis reconnecting', { attempt: times, nextRetryMs: delay }) return delay diff --git a/apps/sim/lib/core/idempotency/cleanup.ts b/apps/sim/lib/core/idempotency/cleanup.ts index 78eb3c08cb..d4d0d11529 100644 --- a/apps/sim/lib/core/idempotency/cleanup.ts +++ b/apps/sim/lib/core/idempotency/cleanup.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { idempotencyKey } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { and, count, inArray, like, lt, max, min, sql } from 'drizzle-orm' @@ -101,8 +102,7 @@ export async function cleanupExpiredIdempotencyKeys( await sleep(100) } } catch (batchError) { - const errorMessage = - batchError instanceof Error ? batchError.message : 'Unknown batch error' + const errorMessage = getErrorMessage(batchError, 'Unknown batch error') logger.error(`Error deleting batch ${batchCount + 1}:`, batchError) errors.push(`Batch ${batchCount + 1}: ${errorMessage}`) @@ -121,7 +121,7 @@ export async function cleanupExpiredIdempotencyKeys( errors: errors.length, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error('Failed to cleanup expired idempotency keys:', error) errors.push(`General error: ${errorMessage}`) } diff --git a/apps/sim/lib/core/idempotency/service.ts b/apps/sim/lib/core/idempotency/service.ts index b5428075a0..540dd83d21 100644 --- a/apps/sim/lib/core/idempotency/service.ts +++ b/apps/sim/lib/core/idempotency/service.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { idempotencyKey } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { eq, lt } from 'drizzle-orm' @@ -448,7 +449,7 @@ export class IdempotencyService { logger.debug(`Successfully completed operation: ${claimResult.normalizedKey}`) return result } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') if (this.config.retryFailures) { await this.deleteKey(claimResult.normalizedKey, claimResult.storageMethod) diff --git a/apps/sim/lib/core/security/csp.test.ts b/apps/sim/lib/core/security/csp.test.ts index 3ee9d41dbf..9f31550226 100644 --- a/apps/sim/lib/core/security/csp.test.ts +++ b/apps/sim/lib/core/security/csp.test.ts @@ -185,7 +185,7 @@ describe('generateRuntimeCSP', () => { }) describe('addCSPSource', () => { - const originalDirectives = JSON.parse(JSON.stringify(buildTimeCSPDirectives)) + const originalDirectives = structuredClone(buildTimeCSPDirectives) afterEach(() => { Object.keys(buildTimeCSPDirectives).forEach((key) => { @@ -222,7 +222,7 @@ describe('addCSPSource', () => { }) describe('removeCSPSource', () => { - const originalDirectives = JSON.parse(JSON.stringify(buildTimeCSPDirectives)) + const originalDirectives = structuredClone(buildTimeCSPDirectives) afterEach(() => { Object.keys(buildTimeCSPDirectives).forEach((key) => { diff --git a/apps/sim/lib/core/security/encryption.ts b/apps/sim/lib/core/security/encryption.ts index 3d2e7390ec..2ffaff4ef2 100644 --- a/apps/sim/lib/core/security/encryption.ts +++ b/apps/sim/lib/core/security/encryption.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { decrypt, encrypt } from '@sim/security/encryption' import { toError } from '@sim/utils/errors' +import { randomInt } from '@sim/utils/random' import { env } from '@/lib/core/config/env' const logger = createLogger('Encryption') @@ -45,7 +46,7 @@ export function generatePassword(length = 24): string { let result = '' for (let i = 0; i < length; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)) + result += chars.charAt(randomInt(0, chars.length)) } return result diff --git a/apps/sim/lib/core/security/redaction.test.ts b/apps/sim/lib/core/security/redaction.test.ts index b5a3c0896a..3c9af9b3ed 100644 --- a/apps/sim/lib/core/security/redaction.test.ts +++ b/apps/sim/lib/core/security/redaction.test.ts @@ -537,7 +537,7 @@ describe('Security edge cases', () => { }, } - const originalCopy = JSON.parse(JSON.stringify(original)) + const originalCopy = structuredClone(original) redactApiKeys(original) expect(original).toEqual(originalCopy) diff --git a/apps/sim/lib/core/telemetry.ts b/apps/sim/lib/core/telemetry.ts index 34af72809f..6751b4fa1e 100644 --- a/apps/sim/lib/core/telemetry.ts +++ b/apps/sim/lib/core/telemetry.ts @@ -18,7 +18,7 @@ import { context, type Span, SpanStatusCode, trace } from '@opentelemetry/api' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { TraceAttr } from '@/lib/copilot/generated/trace-attributes-v1' import type { TraceSpan } from '@/lib/logs/types' @@ -421,7 +421,7 @@ export async function traceBlockExecution( } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR, - message: error instanceof Error ? error.message : 'Block execution failed', + message: getErrorMessage(error, 'Block execution failed'), }) span.recordException(toError(error)) throw error diff --git a/apps/sim/lib/core/utils/logging.ts b/apps/sim/lib/core/utils/logging.ts index 5670d6a5d5..0cf94f82fa 100644 --- a/apps/sim/lib/core/utils/logging.ts +++ b/apps/sim/lib/core/utils/logging.ts @@ -1,3 +1,5 @@ +import { truncate } from '@sim/utils/string' + /** * Sanitize URLs for logging by stripping query/hash and truncating. */ @@ -11,9 +13,9 @@ export function sanitizeUrlForLog(url: string, maxLength = 120): string { const origin = parsed.origin === 'null' ? '' : parsed.origin const sanitized = `${origin}${parsed.pathname}` const result = sanitized || parsed.pathname || trimmed - return result.length > maxLength ? `${result.slice(0, maxLength)}...` : result + return truncate(result, maxLength) } catch { const withoutQuery = trimmed.split('?')[0].split('#')[0] - return withoutQuery.length > maxLength ? `${withoutQuery.slice(0, maxLength)}...` : withoutQuery + return truncate(withoutQuery, maxLength) } } diff --git a/apps/sim/lib/core/utils/with-route-handler.ts b/apps/sim/lib/core/utils/with-route-handler.ts index 8a073ec464..5b3212f23e 100644 --- a/apps/sim/lib/core/utils/with-route-handler.ts +++ b/apps/sim/lib/core/utils/with-route-handler.ts @@ -1,4 +1,5 @@ import { createLogger, runWithRequestContext } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' @@ -33,7 +34,7 @@ export function withRouteHandler(handler: RouteHandler): RouteHandler { response = await handler(request, context) } catch (error) { const duration = Date.now() - startTime - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') logger.error('Unhandled route error', { duration, error: message }) response = NextResponse.json({ error: 'Internal server error', requestId }, { status: 500 }) response?.headers?.set('x-request-id', requestId) diff --git a/apps/sim/lib/data-drains/destinations/bigquery.ts b/apps/sim/lib/data-drains/destinations/bigquery.ts index b7959d75f5..c01779c163 100644 --- a/apps/sim/lib/data-drains/destinations/bigquery.ts +++ b/apps/sim/lib/data-drains/destinations/bigquery.ts @@ -1,13 +1,12 @@ import { createHash } from 'node:crypto' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' +import { backoffWithJitter, parseRetryAfter } from '@sim/utils/retry' import { JWT } from 'google-auth-library' import { z } from 'zod' import { - backoffWithJitter, type ParsedServiceAccount, parseNdjsonObjects, - parseRetryAfter, parseServiceAccount, refineServiceAccountJson, sleepUntilAborted, diff --git a/apps/sim/lib/data-drains/destinations/datadog.ts b/apps/sim/lib/data-drains/destinations/datadog.ts index d9b50d3668..6a56c00ad9 100644 --- a/apps/sim/lib/data-drains/destinations/datadog.ts +++ b/apps/sim/lib/data-drains/destinations/datadog.ts @@ -1,13 +1,9 @@ import { gzipSync } from 'node:zlib' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' +import { backoffWithJitter, parseRetryAfter } from '@sim/utils/retry' import { z } from 'zod' -import { - backoffWithJitter, - parseNdjsonObjects, - parseRetryAfter, - sleepUntilAborted, -} from '@/lib/data-drains/destinations/utils' +import { parseNdjsonObjects, sleepUntilAborted } from '@/lib/data-drains/destinations/utils' import type { DeliveryMetadata, DrainDestination } from '@/lib/data-drains/types' const logger = createLogger('DataDrainDatadogDestination') diff --git a/apps/sim/lib/data-drains/destinations/gcs.ts b/apps/sim/lib/data-drains/destinations/gcs.ts index 492e3bf02b..40e288e711 100644 --- a/apps/sim/lib/data-drains/destinations/gcs.ts +++ b/apps/sim/lib/data-drains/destinations/gcs.ts @@ -1,14 +1,13 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' +import { backoffWithJitter, parseRetryAfter } from '@sim/utils/retry' import { JWT } from 'google-auth-library' import { z } from 'zod' import { - backoffWithJitter, buildObjectKey, normalizePrefix, type ParsedServiceAccount, - parseRetryAfter, parseServiceAccount, refineServiceAccountJson, sleepUntilAborted, diff --git a/apps/sim/lib/data-drains/destinations/snowflake.ts b/apps/sim/lib/data-drains/destinations/snowflake.ts index e931bf9a52..23a8a5665d 100644 --- a/apps/sim/lib/data-drains/destinations/snowflake.ts +++ b/apps/sim/lib/data-drains/destinations/snowflake.ts @@ -2,13 +2,10 @@ import { createHash, createPublicKey } from 'node:crypto' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' +import { backoffWithJitter, parseRetryAfter } from '@sim/utils/retry' import { importPKCS8, SignJWT } from 'jose' import { z } from 'zod' -import { - backoffWithJitter, - parseRetryAfter, - sleepUntilAborted, -} from '@/lib/data-drains/destinations/utils' +import { sleepUntilAborted } from '@/lib/data-drains/destinations/utils' import type { DrainDestination } from '@/lib/data-drains/types' const logger = createLogger('DataDrainSnowflakeDestination') diff --git a/apps/sim/lib/data-drains/destinations/utils.ts b/apps/sim/lib/data-drains/destinations/utils.ts index d9c59f8887..eb220a0439 100644 --- a/apps/sim/lib/data-drains/destinations/utils.ts +++ b/apps/sim/lib/data-drains/destinations/utils.ts @@ -21,73 +21,6 @@ export function sleepUntilAborted(ms: number, signal: AbortSignal): Promise30s is treated - * as a 30s delay so a misconfigured upstream can't stall a drain run. - */ -const RETRY_AFTER_MAX_MS = 30_000 - -/** - * Parses an HTTP `Retry-After` header (either delta-seconds or HTTP-date) into - * a millisecond delay, capped at 30s. Returns `null` when the header is - * absent or unparseable so callers can fall back to their own backoff. - */ -export function parseRetryAfter(header: string | null): number | null { - if (!header) return null - const trimmed = header.trim() - if (trimmed.length === 0) return null - const seconds = Number(trimmed) - if (Number.isFinite(seconds) && seconds >= 0) { - return Math.min(Math.floor(seconds * 1000), RETRY_AFTER_MAX_MS) - } - const dateMs = Date.parse(trimmed) - if (!Number.isNaN(dateMs)) { - const delta = dateMs - Date.now() - if (delta <= 0) return 0 - return Math.min(delta, RETRY_AFTER_MAX_MS) - } - return null -} - export function normalizePrefix(raw: string | undefined): string { if (!raw) return '' const trimmed = raw.replace(/^\/+/, '').replace(/\/+$/, '') diff --git a/apps/sim/lib/data-drains/destinations/webhook.ts b/apps/sim/lib/data-drains/destinations/webhook.ts index 3ea10f828c..4e9cd28955 100644 --- a/apps/sim/lib/data-drains/destinations/webhook.ts +++ b/apps/sim/lib/data-drains/destinations/webhook.ts @@ -1,17 +1,14 @@ import { createHmac } from 'node:crypto' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' +import { backoffWithJitter, parseRetryAfter } from '@sim/utils/retry' import { z } from 'zod' import { validateExternalUrl } from '@/lib/core/security/input-validation' import { secureFetchWithPinnedIP, validateUrlWithDNS, } from '@/lib/core/security/input-validation.server' -import { - backoffWithJitter, - parseRetryAfter, - sleepUntilAborted, -} from '@/lib/data-drains/destinations/utils' +import { sleepUntilAborted } from '@/lib/data-drains/destinations/utils' import type { DeliveryMetadata, DrainDestination } from '@/lib/data-drains/types' const logger = createLogger('DataDrainWebhookDestination') diff --git a/apps/sim/lib/environment/utils.ts b/apps/sim/lib/environment/utils.ts index 6f26baae83..509dcfd766 100644 --- a/apps/sim/lib/environment/utils.ts +++ b/apps/sim/lib/environment/utils.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { environment, workspaceEnvironment } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption' @@ -167,7 +168,7 @@ export async function getPersonalAndWorkspaceEnv( userId, workspaceId, source, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) decryptionFailures.push(k) return [k, ''] as const diff --git a/apps/sim/lib/execution/event-buffer.ts b/apps/sim/lib/execution/event-buffer.ts index 8c0d08090c..5afe0c4cca 100644 --- a/apps/sim/lib/execution/event-buffer.ts +++ b/apps/sim/lib/execution/event-buffer.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' +import { randomInt } from '@sim/utils/random' import { env } from '@/lib/core/config/env' import { getRedisClient } from '@/lib/core/config/redis' import { LARGE_VALUE_THRESHOLD_BYTES } from '@/lib/execution/payloads/large-value-ref' @@ -754,7 +755,7 @@ export function createExecutionEventWriter( FLUSH_INTERVAL_MS * 2 ** Math.min(consecutiveFlushFailures, 6), FLUSH_MAX_RETRY_INTERVAL_MS ) - return backoff + Math.floor(Math.random() * FLUSH_INTERVAL_MS) + return backoff + randomInt(0, FLUSH_INTERVAL_MS) } const scheduleFlush = (delayMs = FLUSH_INTERVAL_MS) => { diff --git a/apps/sim/lib/execution/isolated-vm.test.ts b/apps/sim/lib/execution/isolated-vm.test.ts index d5b4e77586..f19cebf61c 100644 --- a/apps/sim/lib/execution/isolated-vm.test.ts +++ b/apps/sim/lib/execution/isolated-vm.test.ts @@ -8,6 +8,7 @@ import { redisConfigMock, redisConfigMockFns, } from '@sim/testing' +import { sleep } from '@sim/utils/helpers' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' type MockProc = EventEmitter & { @@ -273,7 +274,7 @@ describe('isolated-vm scheduler', () => { ownerKey: 'user:a', }) - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) const second = await executeInIsolatedVM({ code: 'return 2', @@ -310,7 +311,7 @@ describe('isolated-vm scheduler', () => { ownerKey: 'user:hog', }) - await new Promise((resolve) => setTimeout(resolve, 1)) + await sleep(1) const second = await executeInIsolatedVM({ code: 'return 2', diff --git a/apps/sim/lib/execution/isolated-vm.ts b/apps/sim/lib/execution/isolated-vm.ts index 05b23940a9..658cb19afc 100644 --- a/apps/sim/lib/execution/isolated-vm.ts +++ b/apps/sim/lib/execution/isolated-vm.ts @@ -3,7 +3,8 @@ import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' +import { randomFloat } from '@sim/utils/random' import { env } from '@/lib/core/config/env' import { getRedisClient } from '@/lib/core/config/redis' import { @@ -322,7 +323,7 @@ async function secureFetch( url: sanitizeUrlForLog(url), error: toError(error).message, }) - return JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown fetch error' }) + return JSON.stringify({ error: getErrorMessage(error, 'Unknown fetch error') }) } } @@ -720,9 +721,9 @@ function handleBrokerMessage( }) .catch((err) => { logReject('handler_threw', { - error: err instanceof Error ? err.message : String(err), + error: getErrorMessage(err), }) - sendResponse({ error: err instanceof Error ? err.message : String(err) }) + sendResponse({ error: getErrorMessage(err) }) }) } @@ -826,7 +827,7 @@ function handleWorkerMessage(workerId: number, message: unknown) { type: 'fetchResponse', fetchId, response: JSON.stringify({ - error: err instanceof Error ? err.message : 'Fetch failed', + error: getErrorMessage(err, 'Fetch failed'), }), }) } catch (sendErr) { @@ -1336,7 +1337,7 @@ export async function executeInIsolatedVM( } } - const distributedLeaseId = `${req.requestId}:${Date.now()}:${Math.random().toString(36).slice(2, 10)}` + const distributedLeaseId = `${req.requestId}:${Date.now()}:${randomFloat().toString(36).slice(2, 10)}` const leaseAcquireResult = await tryAcquireDistributedLease( ownerKey, distributedLeaseId, diff --git a/apps/sim/lib/execution/payloads/store.ts b/apps/sim/lib/execution/payloads/store.ts index cf1eb00367..8f61961e48 100644 --- a/apps/sim/lib/execution/payloads/store.ts +++ b/apps/sim/lib/execution/payloads/store.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' +import { truncate } from '@sim/utils/string' import { cacheLargeValue, materializeLargeValueRefSync } from '@/lib/execution/payloads/cache' import { LARGE_VALUE_REF_VERSION, @@ -38,7 +39,7 @@ function getKind(value: unknown): LargeValueKind { function getPreview(value: unknown): unknown { if (typeof value === 'string') { - return value.length > 256 ? `${value.slice(0, 256)}...` : value + return truncate(value, 256) } if (Array.isArray(value)) { return { length: value.length } diff --git a/apps/sim/lib/file-parsers/json-parser.ts b/apps/sim/lib/file-parsers/json-parser.ts index ac239fb6e7..2c84cec52a 100644 --- a/apps/sim/lib/file-parsers/json-parser.ts +++ b/apps/sim/lib/file-parsers/json-parser.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import type { FileParseResult } from '@/lib/file-parsers/types' /** @@ -28,7 +29,7 @@ export async function parseJSON(filePath: string): Promise { metadata, } } catch (error) { - throw new Error(`Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`) + throw new Error(`Invalid JSON: ${getErrorMessage(error, 'Unknown error')}`) } } @@ -55,7 +56,7 @@ export async function parseJSONBuffer(buffer: Buffer): Promise metadata, } } catch (error) { - throw new Error(`Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`) + throw new Error(`Invalid JSON: ${getErrorMessage(error, 'Unknown error')}`) } } diff --git a/apps/sim/lib/file-parsers/yaml-parser.ts b/apps/sim/lib/file-parsers/yaml-parser.ts index c055e848d6..7af7743a29 100644 --- a/apps/sim/lib/file-parsers/yaml-parser.ts +++ b/apps/sim/lib/file-parsers/yaml-parser.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import * as yaml from 'js-yaml' import type { FileParseResult } from '@/lib/file-parsers/types' @@ -29,7 +30,7 @@ export async function parseYAML(filePath: string): Promise { metadata, } } catch (error) { - throw new Error(`Invalid YAML: ${error instanceof Error ? error.message : 'Unknown error'}`) + throw new Error(`Invalid YAML: ${getErrorMessage(error, 'Unknown error')}`) } } @@ -56,7 +57,7 @@ export async function parseYAMLBuffer(buffer: Buffer): Promise metadata, } } catch (error) { - throw new Error(`Invalid YAML: ${error instanceof Error ? error.message : 'Unknown error'}`) + throw new Error(`Invalid YAML: ${getErrorMessage(error, 'Unknown error')}`) } } diff --git a/apps/sim/lib/knowledge/connectors/sync-engine.ts b/apps/sim/lib/knowledge/connectors/sync-engine.ts index 22c79598c5..7da40aebd1 100644 --- a/apps/sim/lib/knowledge/connectors/sync-engine.ts +++ b/apps/sim/lib/knowledge/connectors/sync-engine.ts @@ -9,6 +9,7 @@ import { import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' +import { randomInt } from '@sim/utils/random' import { and, eq, gt, inArray, isNull, lt, ne, or, sql } from 'drizzle-orm' import { decryptApiKey } from '@/lib/api-key/crypto' import { getInternalApiBaseUrl } from '@/lib/core/utils/urls' @@ -93,7 +94,7 @@ async function isKnowledgeBaseActiveInTx( function calculateNextSyncTime(syncIntervalMinutes: number): Date | null { if (syncIntervalMinutes <= 0) return null const now = Date.now() - const jitterMs = Math.floor(Math.random() * Math.min(syncIntervalMinutes * 6_000, 300_000)) + const jitterMs = randomInt(0, Math.min(syncIntervalMinutes * 6_000, 300_000)) return new Date(now + syncIntervalMinutes * 60_000 + jitterMs) } diff --git a/apps/sim/lib/knowledge/documents/document-processor.ts b/apps/sim/lib/knowledge/documents/document-processor.ts index a5b5223fba..67f246d06b 100644 --- a/apps/sim/lib/knowledge/documents/document-processor.ts +++ b/apps/sim/lib/knowledge/documents/document-processor.ts @@ -1,6 +1,6 @@ import { randomBytes } from 'crypto' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { PDFDocument } from 'pdf-lib' import { getBYOKKey } from '@/lib/api-key/byok' import { @@ -375,7 +375,7 @@ async function handleFileForOCR( return { httpsUrl, cloudUrl: httpsUrl, buffer } } catch (uploadError) { - const message = uploadError instanceof Error ? uploadError.message : 'Unknown error' + const message = getErrorMessage(uploadError, 'Unknown error') throw new Error(`Cloud upload failed: ${message}. Cloud upload is required for OCR.`) } } diff --git a/apps/sim/lib/knowledge/documents/service.ts b/apps/sim/lib/knowledge/documents/service.ts index 4967560830..204b517911 100644 --- a/apps/sim/lib/knowledge/documents/service.ts +++ b/apps/sim/lib/knowledge/documents/service.ts @@ -8,7 +8,7 @@ import { } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { sha256Hex } from '@sim/security/hash' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { tasks } from '@trigger.dev/sdk' import { @@ -347,9 +347,7 @@ export async function processDocumentsWithQueue( const failures = results.filter((r): r is PromiseRejectedResult => r.status === 'rejected') if (failures.length > 0) { logger.error(`[${requestId}] ${failures.length}/${results.length} document dispatches failed`, { - errors: failures.map((f) => - f.reason instanceof Error ? f.reason.message : String(f.reason) - ), + errors: failures.map((f) => getErrorMessage(f.reason)), }) } @@ -674,7 +672,7 @@ export async function processDocumentAsync( } } catch (error) { const processingTime = Date.now() - startTime - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${documentId}] Failed to process document after ${processingTime}ms:`, { error: errorMessage, stack: error instanceof Error ? error.stack : undefined, @@ -743,7 +741,7 @@ async function processDocumentsWithTrigger( return { success: false, - message: error instanceof Error ? error.message : 'Failed to trigger background jobs', + message: getErrorMessage(error, 'Failed to trigger background jobs'), } } } diff --git a/apps/sim/lib/knowledge/documents/utils.ts b/apps/sim/lib/knowledge/documents/utils.ts index 597c64602f..0a2221cdc3 100644 --- a/apps/sim/lib/knowledge/documents/utils.ts +++ b/apps/sim/lib/knowledge/documents/utils.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' +import { randomFloat } from '@sim/utils/random' const logger = createLogger('RetryUtils') @@ -142,7 +143,7 @@ export async function retryWithExponentialBackoff( ) } - const jitter = Math.random() * 0.1 * delay + const jitter = randomFloat() * 0.1 * delay const actualDelay = cappedRetryAfter ?? Math.min(delay + jitter, maxDelayMs) logger.info( diff --git a/apps/sim/lib/mcp/client.ts b/apps/sim/lib/mcp/client.ts index 0918750b15..93588aecdd 100644 --- a/apps/sim/lib/mcp/client.ts +++ b/apps/sim/lib/mcp/client.ts @@ -16,6 +16,7 @@ import { ToolListChangedNotificationSchema, } from '@modelcontextprotocol/sdk/types.js' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { getMaxExecutionTimeout } from '@/lib/core/execution-limits' import { type McpClientOptions, @@ -131,7 +132,7 @@ export class McpClient { protocolVersion: serverVersion, }) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') this.connectionStatus.lastError = errorMessage this.isConnected = false logger.error(`Failed to connect to MCP server ${this.config.name}:`, error) diff --git a/apps/sim/lib/mcp/service.ts b/apps/sim/lib/mcp/service.ts index 943ad4d803..4ec764c3d4 100644 --- a/apps/sim/lib/mcp/service.ts +++ b/apps/sim/lib/mcp/service.ts @@ -5,7 +5,7 @@ import { db } from '@sim/db' import { mcpServers } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { and, eq, isNull } from 'drizzle-orm' import { isTest } from '@/lib/core/config/feature-flags' @@ -380,8 +380,7 @@ class McpService { ) } else { failedCount++ - const errorMessage = - result.reason instanceof Error ? result.reason.message : 'Unknown error' + const errorMessage = getErrorMessage(result.reason, 'Unknown error') logger.warn(`[${requestId}] Failed to discover tools from server ${server.name}:`) statusUpdates.push(this.updateServerStatus(server.id!, workspaceId, false, errorMessage)) } @@ -515,7 +514,7 @@ class McpService { status: 'error', toolCount: 0, lastSeen: undefined, - error: error instanceof Error ? error.message : 'Connection failed', + error: getErrorMessage(error, 'Connection failed'), }) } } diff --git a/apps/sim/lib/mcp/storage/memory-cache.test.ts b/apps/sim/lib/mcp/storage/memory-cache.test.ts index 0db1792435..c97944288f 100644 --- a/apps/sim/lib/mcp/storage/memory-cache.test.ts +++ b/apps/sim/lib/mcp/storage/memory-cache.test.ts @@ -1,3 +1,4 @@ +import { sleep } from '@sim/utils/helpers' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import type { McpTool } from '@/lib/mcp/types' import { MemoryMcpCache } from './memory-cache' @@ -43,7 +44,7 @@ describe('MemoryMcpCache', () => { await cache.set('key-1', tools, 0) // Wait a tiny bit to ensure expiry - await new Promise((resolve) => setTimeout(resolve, 5)) + await sleep(5) const result = await cache.get('key-1') expect(result).toBeNull() @@ -54,7 +55,7 @@ describe('MemoryMcpCache', () => { await cache.set('key-1', tools, 1) // 1ms TTL // Wait for expiry - await new Promise((resolve) => setTimeout(resolve, 10)) + await sleep(10) // First get should return null and remove entry await cache.get('key-1') @@ -242,7 +243,7 @@ describe('MemoryMcpCache', () => { await cache.set('key-1', tools, 1) // 1 millisecond // Wait past expiry - await new Promise((resolve) => setTimeout(resolve, 10)) + await sleep(10) const result = await cache.get('key-1') expect(result).toBeNull() diff --git a/apps/sim/lib/messaging/email/mailer.ts b/apps/sim/lib/messaging/email/mailer.ts index ae7d4579cf..06f7086914 100644 --- a/apps/sim/lib/messaging/email/mailer.ts +++ b/apps/sim/lib/messaging/email/mailer.ts @@ -1,5 +1,6 @@ import { EmailClient, type EmailMessage } from '@azure/communication-email' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Resend } from 'resend' import { env } from '@/lib/core/config/env' import { getBaseUrl } from '@/lib/core/utils/urls' @@ -345,7 +346,7 @@ export async function sendBatchEmails(options: BatchEmailOptions): Promise { } catch (error) { results.push({ success: false, - message: error instanceof Error ? error.message : 'Failed to send SMS', + message: getErrorMessage(error, 'Failed to send SMS'), }) } } @@ -157,7 +158,7 @@ async function sendBatchSMS(options: BatchSMSOptions): Promise { } catch (error) { logger.error('Inbox task execution failed', { taskId, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) - await markTaskFailed(taskId, error instanceof Error ? error.message : 'Execution failed') + await markTaskFailed(taskId, getErrorMessage(error, 'Execution failed')) if (!responseSent) { try { @@ -283,7 +284,7 @@ export async function executeInboxTask(taskId: string): Promise { { success: false, content: '', - error: error instanceof Error ? error.message : 'Execution failed', + error: getErrorMessage(error, 'Execution failed'), }, { inboxProviderId: ws.inboxProviderId, workspaceId: ws.id } ) @@ -352,7 +353,7 @@ async function persistChatMessages( } catch (err) { logger.warn('Failed to persist chat messages', { chatId, - error: err instanceof Error ? err.message : 'Unknown error', + error: getErrorMessage(err, 'Unknown error'), }) } } @@ -460,7 +461,7 @@ async function downloadAttachmentContents( taskId, attachmentId: attachment.attachment_id, filename: attachment.filename, - error: outcome.reason instanceof Error ? outcome.reason.message : 'Unknown error', + error: getErrorMessage(outcome.reason, 'Unknown error'), }) } } diff --git a/apps/sim/lib/mothership/inbox/response.ts b/apps/sim/lib/mothership/inbox/response.ts index aa241d05f1..f289ec705c 100644 --- a/apps/sim/lib/mothership/inbox/response.ts +++ b/apps/sim/lib/mothership/inbox/response.ts @@ -2,6 +2,7 @@ import { type ComponentType, type CSSProperties, createElement, type ReactNode } import { Body, Head, Html, Link, Markdown, Section, Text } from '@react-email/components' import { render } from '@react-email/render' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { getBaseUrl } from '@/lib/core/utils/urls' import * as agentmail from '@/lib/mothership/inbox/agentmail-client' import { replaceUntilStable } from '@/lib/mothership/inbox/format' @@ -54,7 +55,7 @@ export async function sendInboxResponse( } catch (error) { logger.error('Failed to send inbox response email', { taskId: inboxTask.id, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) return null } diff --git a/apps/sim/lib/pptx-renderer/core/viewer.ts b/apps/sim/lib/pptx-renderer/core/viewer.ts index 23ea7498cb..6c401a7876 100644 --- a/apps/sim/lib/pptx-renderer/core/viewer.ts +++ b/apps/sim/lib/pptx-renderer/core/viewer.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import type { ECharts } from 'echarts' import { buildPresentation, type PresentationData } from '../model/presentation' import type { ZipParseLimits } from '../parser/zip-parser' @@ -661,7 +662,7 @@ export class PptxViewer extends EventTarget { wrapper.style.border = '2px dashed #ff6b6b' wrapper.style.color = '#cc0000' wrapper.style.fontSize = '14px' - wrapper.textContent = `Slide ${index + 1}: Render Error - ${e instanceof Error ? e.message : String(e)}` + wrapper.textContent = `Slide ${index + 1}: Render Error - ${getErrorMessage(e)}` } } @@ -881,7 +882,7 @@ export class PptxViewer extends EventTarget { wrapper.style.border = '2px dashed #ff6b6b' wrapper.style.color = '#cc0000' wrapper.style.fontSize = '14px' - wrapper.textContent = `Slide ${this.currentSlide + 1}: Render Error - ${e instanceof Error ? e.message : String(e)}` + wrapper.textContent = `Slide ${this.currentSlide + 1}: Render Error - ${getErrorMessage(e)}` } this.container.appendChild(wrapper) diff --git a/apps/sim/lib/table/constants.ts b/apps/sim/lib/table/constants.ts index 67f01eb419..a56ef2ca9e 100644 --- a/apps/sim/lib/table/constants.ts +++ b/apps/sim/lib/table/constants.ts @@ -2,6 +2,7 @@ * Limits and constants for user-defined tables. */ +import { randomInt, randomItem } from '@sim/utils/random' import { env, envNumber } from '@/lib/core/config/env' export const TABLE_LIMITS = { @@ -311,14 +312,14 @@ export function generateUniqueTableName(existingNames: string[]): string { const maxAttempts = 50 for (let i = 0; i < maxAttempts; i++) { - const adj = TABLE_NAME_ADJECTIVES[Math.floor(Math.random() * TABLE_NAME_ADJECTIVES.length)] - const noun = TABLE_NAME_NOUNS[Math.floor(Math.random() * TABLE_NAME_NOUNS.length)] + const adj = randomItem(TABLE_NAME_ADJECTIVES) + const noun = randomItem(TABLE_NAME_NOUNS) const name = `${adj.toLowerCase()}_${noun.toLowerCase()}` if (!taken.has(name)) return name } - const adj = TABLE_NAME_ADJECTIVES[Math.floor(Math.random() * TABLE_NAME_ADJECTIVES.length)] - const noun = TABLE_NAME_NOUNS[Math.floor(Math.random() * TABLE_NAME_NOUNS.length)] - const suffix = Math.floor(Math.random() * 900) + 100 + const adj = randomItem(TABLE_NAME_ADJECTIVES) + const noun = randomItem(TABLE_NAME_NOUNS) + const suffix = randomInt(100, 1000) return `${adj.toLowerCase()}_${noun.toLowerCase()}_${suffix}` } diff --git a/apps/sim/lib/uploads/contexts/execution/execution-file-manager.ts b/apps/sim/lib/uploads/contexts/execution/execution-file-manager.ts index 4665b6fc22..1fb9c43509 100644 --- a/apps/sim/lib/uploads/contexts/execution/execution-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/execution/execution-file-manager.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { isUserFileWithMetadata } from '@/lib/core/utils/user-file' import { StorageService } from '@/lib/uploads' import type { ExecutionContext } from '@/lib/uploads/contexts/execution/utils' @@ -122,9 +123,7 @@ export async function uploadExecutionFile( return userFile } catch (error) { logger.error(`Failed to upload execution file ${fileName}:`, error) - throw new Error( - `Failed to upload file: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to upload file: ${getErrorMessage(error, 'Unknown error')}`) } } @@ -146,9 +145,7 @@ export async function downloadExecutionFile(userFile: UserFile): Promise return fileBuffer } catch (error) { logger.error(`Failed to download execution file ${userFile.name}:`, error) - throw new Error( - `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to download file: ${getErrorMessage(error, 'Unknown error')}`) } } diff --git a/apps/sim/lib/uploads/contexts/execution/utils.ts b/apps/sim/lib/uploads/contexts/execution/utils.ts index 94c35b2ade..4cda4b6441 100644 --- a/apps/sim/lib/uploads/contexts/execution/utils.ts +++ b/apps/sim/lib/uploads/contexts/execution/utils.ts @@ -1,3 +1,4 @@ +import { randomFloat } from '@sim/utils/random' import { isUuid, sanitizeFileName } from '@/executor/constants' import type { UserFile } from '@/executor/types' @@ -24,7 +25,7 @@ export function generateExecutionFileKey(context: ExecutionContext, fileName: st * Generate unique file ID for execution files */ export function generateFileId(): string { - return `file_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` + return `file_${Date.now()}_${randomFloat().toString(36).substring(2, 9)}` } /** diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts index 3738eea03e..89b3bd37d9 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -7,7 +7,7 @@ import { randomBytes } from 'crypto' import { db } from '@sim/db' import { workspaceFiles } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/errors' +import { getErrorMessage, getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { and, eq, isNull, sql } from 'drizzle-orm' import { @@ -286,9 +286,7 @@ export async function uploadWorkspaceFile( continue } logger.error(`Failed to upload workspace file ${fileName}:`, error) - throw new Error( - `Failed to upload file: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to upload file: ${getErrorMessage(error, 'Unknown error')}`) } } @@ -853,9 +851,7 @@ export async function fetchWorkspaceFileBuffer(fileRecord: WorkspaceFileRecord): return buffer } catch (error) { logger.error(`Failed to download workspace file ${fileRecord.name}:`, error) - throw new Error( - `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to download file: ${getErrorMessage(error, 'Unknown error')}`) } } @@ -938,9 +934,7 @@ export async function updateWorkspaceFileContent( } } catch (error) { logger.error(`Failed to update workspace file content ${fileId}:`, error) - throw new Error( - `Failed to update file content: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to update file content: ${getErrorMessage(error, 'Unknown error')}`) } } @@ -1030,9 +1024,7 @@ export async function deleteWorkspaceFile(workspaceId: string, fileId: string): logger.info(`Successfully archived workspace file: ${fileRecord.name}`) } catch (error) { logger.error(`Failed to delete workspace file ${fileId}:`, error) - throw new Error( - `Failed to delete file: ${error instanceof Error ? error.message : 'Unknown error'}` - ) + throw new Error(`Failed to delete file: ${getErrorMessage(error, 'Unknown error')}`) } } diff --git a/apps/sim/lib/uploads/utils/file-utils.server.ts b/apps/sim/lib/uploads/utils/file-utils.server.ts index 69077c663d..5d62fb4be7 100644 --- a/apps/sim/lib/uploads/utils/file-utils.server.ts +++ b/apps/sim/lib/uploads/utils/file-utils.server.ts @@ -1,6 +1,7 @@ 'use server' import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { getMaxExecutionTimeout } from '@/lib/core/execution-limits' import { secureFetchWithPinnedIP, @@ -62,7 +63,7 @@ export async function resolveFileInputToUrl( return { error: { status: 400, - message: error instanceof Error ? error.message : 'Failed to process file', + message: getErrorMessage(error, 'Failed to process file'), }, } } diff --git a/apps/sim/lib/webhooks/polling/gmail.ts b/apps/sim/lib/webhooks/polling/gmail.ts index 06abed145f..cc85dc8c93 100644 --- a/apps/sim/lib/webhooks/polling/gmail.ts +++ b/apps/sim/lib/webhooks/polling/gmail.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { getProviderConfig, @@ -245,7 +246,7 @@ async function fetchNewEmails( return { emails, latestHistoryId } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error fetching new emails:`, errorMessage) throw error } @@ -375,7 +376,7 @@ async function searchEmails( return { emails, latestHistoryId } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error searching emails:`, errorMessage) throw error } @@ -564,7 +565,7 @@ async function processEmails( ) processedCount++ } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error processing email ${email.id}:`, errorMessage) failedCount++ } diff --git a/apps/sim/lib/webhooks/polling/google-calendar.ts b/apps/sim/lib/webhooks/polling/google-calendar.ts index b417ec4cd1..f7740127b1 100644 --- a/apps/sim/lib/webhooks/polling/google-calendar.ts +++ b/apps/sim/lib/webhooks/polling/google-calendar.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { getProviderConfig, @@ -348,7 +349,7 @@ async function processEvents( ) processedCount++ } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error processing event ${event.id}:`, errorMessage) failedCount++ } diff --git a/apps/sim/lib/webhooks/polling/google-drive.ts b/apps/sim/lib/webhooks/polling/google-drive.ts index 7bc2a8051e..ecd343d1e4 100644 --- a/apps/sim/lib/webhooks/polling/google-drive.ts +++ b/apps/sim/lib/webhooks/polling/google-drive.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { getProviderConfig, @@ -424,7 +425,7 @@ async function processChanges( ) processedCount++ } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error( `[${requestId}] Error processing change for file ${change.fileId}:`, errorMessage diff --git a/apps/sim/lib/webhooks/polling/google-sheets.ts b/apps/sim/lib/webhooks/polling/google-sheets.ts index 81376c54d2..6905d36c06 100644 --- a/apps/sim/lib/webhooks/polling/google-sheets.ts +++ b/apps/sim/lib/webhooks/polling/google-sheets.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { getProviderConfig, @@ -456,7 +457,7 @@ async function processRows( ) processedCount++ } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error processing row ${rowNumber}:`, errorMessage) failedCount++ } diff --git a/apps/sim/lib/webhooks/polling/imap.ts b/apps/sim/lib/webhooks/polling/imap.ts index 15443a63da..596f81f3a5 100644 --- a/apps/sim/lib/webhooks/polling/imap.ts +++ b/apps/sim/lib/webhooks/polling/imap.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type { FetchMessageObject, MailboxLockObject } from 'imapflow' import { ImapFlow } from 'imapflow' import { pollingIdempotency } from '@/lib/core/idempotency/service' @@ -590,7 +591,7 @@ async function processEmails( ) processedCount++ } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error processing email ${email.uid}:`, errorMessage) failedCount++ } diff --git a/apps/sim/lib/webhooks/polling/outlook.ts b/apps/sim/lib/webhooks/polling/outlook.ts index 19a52f16d7..660c81f497 100644 --- a/apps/sim/lib/webhooks/polling/outlook.ts +++ b/apps/sim/lib/webhooks/polling/outlook.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { htmlToText } from 'html-to-text' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { fetchWithRetry } from '@/lib/knowledge/documents/utils' @@ -271,7 +272,7 @@ async function fetchNewOutlookEmails( return { emails: filteredEmails } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error fetching new Outlook emails:`, errorMessage) throw error } diff --git a/apps/sim/lib/webhooks/polling/rss.ts b/apps/sim/lib/webhooks/polling/rss.ts index 4009c70ae8..15de4122d0 100644 --- a/apps/sim/lib/webhooks/polling/rss.ts +++ b/apps/sim/lib/webhooks/polling/rss.ts @@ -1,4 +1,5 @@ import type { Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import Parser from 'rss-parser' import { pollingIdempotency } from '@/lib/core/idempotency/service' import { @@ -278,7 +279,7 @@ async function fetchNewRssItems( lastModified: newLastModified, } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error fetching RSS feed:`, errorMessage) throw error } @@ -363,7 +364,7 @@ async function processRssItems( ) processedCount++ } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') logger.error(`[${requestId}] Error processing item:`, errorMessage) failedCount++ } diff --git a/apps/sim/lib/webhooks/providers/stripe.ts b/apps/sim/lib/webhooks/providers/stripe.ts index 449844042c..ee35e7df12 100644 --- a/apps/sim/lib/webhooks/providers/stripe.ts +++ b/apps/sim/lib/webhooks/providers/stripe.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { NextResponse } from 'next/server' import Stripe from 'stripe' import type { @@ -32,7 +33,7 @@ export const stripeHandler: WebhookProviderHandler = { Stripe.webhooks.constructEvent(rawBody, signature, secret) } catch (error) { logger.warn(`[${requestId}] Stripe signature verification failed`, { - error: error instanceof Error ? error.message : String(error), + error: getErrorMessage(error), }) return new NextResponse('Unauthorized - Invalid Stripe signature', { status: 401 }) } diff --git a/apps/sim/lib/webhooks/utils.server.ts b/apps/sim/lib/webhooks/utils.server.ts index 84698d05cf..a920553f16 100644 --- a/apps/sim/lib/webhooks/utils.server.ts +++ b/apps/sim/lib/webhooks/utils.server.ts @@ -1,6 +1,7 @@ import { db, workflowDeploymentVersion } from '@sim/db' import { webhook, workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { and, eq, isNull, or } from 'drizzle-orm' import type { DbOrTx } from '@/lib/db/types' @@ -223,7 +224,7 @@ export async function syncWebhooksForCredentialSet(params: { ) } } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') syncLogger.error( `[${requestId}] Failed to sync webhook for credential ${cred.credentialId}: ${errorMessage}` ) @@ -247,7 +248,7 @@ export async function syncWebhooksForCredentialSet(params: { `[${requestId}] Deleted webhook ${existingWebhook.id} for removed credential ${credentialId}` ) } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' + const errorMessage = getErrorMessage(error, 'Unknown error') syncLogger.error( `[${requestId}] Failed to delete webhook ${existingWebhook.id} for credential ${credentialId}: ${errorMessage}` ) diff --git a/apps/sim/lib/workflows/autolayout/index.ts b/apps/sim/lib/workflows/autolayout/index.ts index b362189941..2bbfca7f99 100644 --- a/apps/sim/lib/workflows/autolayout/index.ts +++ b/apps/sim/lib/workflows/autolayout/index.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { DEFAULT_HORIZONTAL_SPACING, DEFAULT_VERTICAL_SPACING, @@ -31,7 +32,7 @@ export function applyAutoLayout( edgeCount: edges.length, }) - const blocksCopy: Record = JSON.parse(JSON.stringify(blocks)) + const blocksCopy: Record = structuredClone(blocks) const horizontalSpacing = options.horizontalSpacing ?? DEFAULT_HORIZONTAL_SPACING const verticalSpacing = options.verticalSpacing ?? DEFAULT_VERTICAL_SPACING @@ -86,7 +87,7 @@ export function applyAutoLayout( return { blocks, success: false, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), } } } diff --git a/apps/sim/lib/workflows/autolayout/targeted.ts b/apps/sim/lib/workflows/autolayout/targeted.ts index 33c0fb3f34..e6021885fe 100644 --- a/apps/sim/lib/workflows/autolayout/targeted.ts +++ b/apps/sim/lib/workflows/autolayout/targeted.ts @@ -62,7 +62,7 @@ export function applyTargetedLayout( const changedSet = new Set(changedBlockIds) const resizedSet = new Set(resizedBlockIds) const shiftSourceSet = new Set(shiftSourceBlockIds) - const blocksCopy: Record = JSON.parse(JSON.stringify(blocks)) + const blocksCopy: Record = structuredClone(blocks) prepareContainerDimensions( blocksCopy, diff --git a/apps/sim/lib/workflows/colors.ts b/apps/sim/lib/workflows/colors.ts index d7acbb74ab..0b16c72b8b 100644 --- a/apps/sim/lib/workflows/colors.ts +++ b/apps/sim/lib/workflows/colors.ts @@ -11,6 +11,8 @@ * - Pink: (#ec4899) */ +import { randomItem } from '@sim/utils/random' + /** * Full list of available workflow colors with names. * Used for color picker and random color assignment. @@ -69,5 +71,5 @@ export const WORKFLOW_COLORS = [ * @returns A hex color string from the available workflow colors */ export function getNextWorkflowColor(): string { - return WORKFLOW_COLORS[Math.floor(Math.random() * WORKFLOW_COLORS.length)].color + return randomItem(WORKFLOW_COLORS).color } diff --git a/apps/sim/lib/workflows/comparison/resolve-values.ts b/apps/sim/lib/workflows/comparison/resolve-values.ts index e66d76b8bc..651a03ad50 100644 --- a/apps/sim/lib/workflows/comparison/resolve-values.ts +++ b/apps/sim/lib/workflows/comparison/resolve-values.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { truncate } from '@sim/utils/string' import { buildSelectorContextFromBlock } from '@/lib/workflows/subblocks/context' import { getBlock } from '@/blocks/registry' import { SELECTOR_TYPES_HYDRATION_REQUIRED, type SubBlockConfig } from '@/blocks/types' @@ -160,7 +161,7 @@ function resolveDropdownLabel(subBlockConfig: SubBlockConfig, value: string): st export function formatValueForDisplay(value: unknown): string { if (value === null || value === undefined) return '(none)' if (typeof value === 'string') { - if (value.length > 50) return `${value.slice(0, 50)}...` + if (value.length > 50) return truncate(value, 50) return value || '(empty)' } if (typeof value === 'boolean') return value ? 'enabled' : 'disabled' @@ -168,7 +169,7 @@ export function formatValueForDisplay(value: unknown): string { if (Array.isArray(value)) return `[${value.length} items]` if (typeof value === 'object') { const json = JSON.stringify(value) - return json.length > 50 ? `${json.slice(0, 50)}...` : json + return truncate(json, 50) } return String(value) } diff --git a/apps/sim/lib/workflows/credentials/credential-extractor.ts b/apps/sim/lib/workflows/credentials/credential-extractor.ts index 3ec6c73bb6..9540c5891d 100644 --- a/apps/sim/lib/workflows/credentials/credential-extractor.ts +++ b/apps/sim/lib/workflows/credentials/credential-extractor.ts @@ -238,7 +238,7 @@ export function sanitizeWorkflowForSharing( preserveEnvVars?: boolean // Keep {{VAR}} references for export } = {} ): SanitizedWorkflowState { - const sanitized = JSON.parse(JSON.stringify(state)) as SanitizedWorkflowState // Deep clone + const sanitized = structuredClone(state) as SanitizedWorkflowState if (!sanitized?.blocks) { return sanitized diff --git a/apps/sim/lib/workflows/diff/diff-engine.ts b/apps/sim/lib/workflows/diff/diff-engine.ts index d805945254..670ac63ca7 100644 --- a/apps/sim/lib/workflows/diff/diff-engine.ts +++ b/apps/sim/lib/workflows/diff/diff-engine.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import type { Edge } from 'reactflow' import { getTargetedLayoutImpact } from '@/lib/workflows/autolayout' @@ -214,7 +214,7 @@ export class WorkflowDiffEngine { logger.error('Failed to create diff:', error) return { success: false, - errors: [error instanceof Error ? error.message : 'Failed to create diff'], + errors: [getErrorMessage(error, 'Failed to create diff')], } } } @@ -690,9 +690,7 @@ export class WorkflowDiffEngine { logger.error('Failed to create diff from workflow state:', error) return { success: false, - errors: [ - error instanceof Error ? error.message : 'Failed to create diff from workflow state', - ], + errors: [getErrorMessage(error, 'Failed to create diff from workflow state')], } } } @@ -730,7 +728,7 @@ export class WorkflowDiffEngine { logger.error('Failed to merge diff:', error) return { success: false, - errors: [error instanceof Error ? error.message : 'Failed to merge diff'], + errors: [getErrorMessage(error, 'Failed to merge diff')], } } } diff --git a/apps/sim/lib/workflows/executor/execution-core.ts b/apps/sim/lib/workflows/executor/execution-core.ts index c099ce3151..589c4e3236 100644 --- a/apps/sim/lib/workflows/executor/execution-core.ts +++ b/apps/sim/lib/workflows/executor/execution-core.ts @@ -4,6 +4,7 @@ */ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { mergeSubblockStateWithValues } from '@sim/workflow-persistence/subblocks' import type { Edge } from 'reactflow' import { z } from 'zod' @@ -236,7 +237,7 @@ async function finalizeExecutionError(params: { endedAt: new Date().toISOString(), totalDurationMs: executionResult?.metadata?.duration || 0, error: { - message: error instanceof Error ? error.message : 'Execution failed', + message: getErrorMessage(error, 'Execution failed'), stackTrace: error instanceof Error ? error.stack : undefined, }, traceSpans, diff --git a/apps/sim/lib/workflows/operations/import-export.ts b/apps/sim/lib/workflows/operations/import-export.ts index 8d54ec72df..7a4b7577ac 100644 --- a/apps/sim/lib/workflows/operations/import-export.ts +++ b/apps/sim/lib/workflows/operations/import-export.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { ApiClientError } from '@/lib/api/client/errors' import { requestJson } from '@/lib/api/client/request' @@ -518,9 +519,7 @@ export function parseWorkflowJson( try { data = JSON.parse(jsonContent) } catch (parseError) { - errors.push( - `Invalid JSON: ${parseError instanceof Error ? parseError.message : 'Parse error'}` - ) + errors.push(`Invalid JSON: ${getErrorMessage(parseError, 'Parse error')}`) return { data: null, errors } } @@ -642,7 +641,7 @@ export function parseWorkflowJson( return { data: workflowState, errors: [] } } catch (error) { logger.error('Failed to parse workflow JSON:', error) - errors.push(`Unexpected error: ${error instanceof Error ? error.message : 'Unknown error'}`) + errors.push(`Unexpected error: ${getErrorMessage(error, 'Unknown error')}`) return { data: null, errors } } } diff --git a/apps/sim/lib/workflows/persistence/duplicate.ts b/apps/sim/lib/workflows/persistence/duplicate.ts index 45a188130c..b6f6709db8 100644 --- a/apps/sim/lib/workflows/persistence/duplicate.ts +++ b/apps/sim/lib/workflows/persistence/duplicate.ts @@ -580,9 +580,7 @@ export async function duplicateWorkflow( | LoopConfig | ParallelConfig if (subflow.config && typeof subflow.config === 'object') { - updatedConfig = JSON.parse(JSON.stringify(subflow.config)) as - | LoopConfig - | ParallelConfig + updatedConfig = structuredClone(subflow.config) as LoopConfig | ParallelConfig // Update the config ID to match the new subflow ID diff --git a/apps/sim/lib/workflows/persistence/utils.test.ts b/apps/sim/lib/workflows/persistence/utils.test.ts index e6b9dbb086..1059c61caf 100644 --- a/apps/sim/lib/workflows/persistence/utils.test.ts +++ b/apps/sim/lib/workflows/persistence/utils.test.ts @@ -767,7 +767,7 @@ describe('Database Helpers', () => { mockDb.transaction = mockTransaction - const staleWorkflowState = JSON.parse(JSON.stringify(mockWorkflowState)) + const staleWorkflowState = structuredClone(mockWorkflowState) staleWorkflowState.loops = {} staleWorkflowState.parallels = {} diff --git a/apps/sim/lib/workflows/persistence/utils.ts b/apps/sim/lib/workflows/persistence/utils.ts index bea2f042e1..d30b03d531 100644 --- a/apps/sim/lib/workflows/persistence/utils.ts +++ b/apps/sim/lib/workflows/persistence/utils.ts @@ -1,6 +1,7 @@ import { db, workflow, workflowDeploymentVersion } from '@sim/db' import { credential } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { getActiveWorkflowContext } from '@sim/workflow-authz' import { @@ -470,7 +471,7 @@ export async function saveWorkflowToNormalizedTables( return saveWorkflowToNormalizedTablesRaw(workflowId, state, tx) }) } catch (error) { - const message = error instanceof Error ? error.message : 'Failed to save workflow state' + const message = getErrorMessage(error, 'Failed to save workflow state') logger.error(`Error saving workflow ${workflowId} to normalized tables:`, error) return { success: false, error: message } } @@ -659,7 +660,7 @@ export async function deployWorkflow(params: { logger.error(`Error deploying workflow ${workflowId}:`, error) return { success: false, - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), } } } @@ -716,7 +717,7 @@ export function regenerateWorkflowStateIds(state: RegenerateStateInput): Regener const newBlock: BlockState = { ...block, id: newId, - subBlocks: JSON.parse(JSON.stringify(block.subBlocks)), + subBlocks: structuredClone(block.subBlocks), locked: false, } @@ -864,7 +865,7 @@ export async function undeployWorkflow(params: { logger.error(`Error undeploying workflow ${workflowId}:`, error) return { success: false, - error: error instanceof Error ? error.message : 'Failed to undeploy workflow', + error: getErrorMessage(error, 'Failed to undeploy workflow'), } } } @@ -970,7 +971,7 @@ export async function activateWorkflowVersion(params: { logger.error(`Error activating version ${version} for workflow ${workflowId}:`, error) return { success: false, - error: error instanceof Error ? error.message : 'Failed to activate version', + error: getErrorMessage(error, 'Failed to activate version'), } } } @@ -1065,7 +1066,7 @@ async function activateWorkflowVersionById(params: { ) return { success: false, - error: error instanceof Error ? error.message : 'Failed to activate version', + error: getErrorMessage(error, 'Failed to activate version'), } } } diff --git a/apps/sim/lib/workflows/schedules/deploy.ts b/apps/sim/lib/workflows/schedules/deploy.ts index 5222499fc3..00d6e7075a 100644 --- a/apps/sim/lib/workflows/schedules/deploy.ts +++ b/apps/sim/lib/workflows/schedules/deploy.ts @@ -1,5 +1,6 @@ import { db, workflowSchedule } from '@sim/db' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull } from 'drizzle-orm' import type { DbOrTx } from '@/lib/db/types' @@ -162,7 +163,7 @@ export async function createSchedulesForDeploy( logger.error(`Failed to create schedules for workflow ${workflowId}`, error) return { success: false, - error: error instanceof Error ? error.message : 'Failed to create schedules', + error: getErrorMessage(error, 'Failed to create schedules'), } } diff --git a/apps/sim/lib/workflows/schedules/utils.ts b/apps/sim/lib/workflows/schedules/utils.ts index 6a82d4d4c6..3639cb7cd5 100644 --- a/apps/sim/lib/workflows/schedules/utils.ts +++ b/apps/sim/lib/workflows/schedules/utils.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { formatDateTime } from '@sim/utils/formatting' import { Cron } from 'croner' import cronstrue from 'cronstrue' @@ -45,7 +45,7 @@ export function validateCronExpression( } catch (error) { return { isValid: false, - error: error instanceof Error ? error.message : 'Invalid cron expression syntax', + error: getErrorMessage(error, 'Invalid cron expression syntax'), } } } diff --git a/apps/sim/lib/workflows/schedules/validation.ts b/apps/sim/lib/workflows/schedules/validation.ts index 2c8356c0bc..6ba56f5d5b 100644 --- a/apps/sim/lib/workflows/schedules/validation.ts +++ b/apps/sim/lib/workflows/schedules/validation.ts @@ -2,6 +2,7 @@ * Client-safe schedule validation functions * These can be used in both client and server contexts */ +import { getErrorMessage } from '@sim/utils/errors' import { type BlockState, calculateNextRunTime, @@ -161,7 +162,7 @@ export function validateScheduleBlock(block: BlockState): ScheduleValidationResu } catch (error) { return { isValid: false, - error: error instanceof Error ? error.message : 'Failed to generate schedule', + error: getErrorMessage(error, 'Failed to generate schedule'), scheduleType, } } diff --git a/apps/sim/lib/workflows/streaming/streaming.ts b/apps/sim/lib/workflows/streaming/streaming.ts index 3336f17a9c..f33dd23947 100644 --- a/apps/sim/lib/workflows/streaming/streaming.ts +++ b/apps/sim/lib/workflows/streaming/streaming.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { createTimeoutAbortController, getTimeoutErrorMessage } from '@/lib/core/execution-limits' import { extractBlockIdFromOutputId, @@ -284,7 +285,7 @@ export async function createStreamingResponse( encodeSSE({ event: 'stream_error', blockId, - error: error instanceof Error ? error.message : 'Stream reading error', + error: getErrorMessage(error, 'Stream reading error'), }) ) } diff --git a/apps/sim/lib/workspaces/colors.ts b/apps/sim/lib/workspaces/colors.ts index b652ebd36d..148ac7c022 100644 --- a/apps/sim/lib/workspaces/colors.ts +++ b/apps/sim/lib/workspaces/colors.ts @@ -1,3 +1,5 @@ +import { randomItem } from '@sim/utils/random' + /** Color palette for workspace accents, aligned with the workflow color family. */ export const WORKSPACE_COLORS = [ '#2ABBF8', // Blue @@ -11,7 +13,7 @@ export const WORKSPACE_COLORS = [ /** Picks a random workspace color from the hero palette. */ export function getRandomWorkspaceColor(): string { - return WORKSPACE_COLORS[Math.floor(Math.random() * WORKSPACE_COLORS.length)] + return randomItem(WORKSPACE_COLORS) } const APP_COLORS = [ diff --git a/apps/sim/lib/workspaces/naming.ts b/apps/sim/lib/workspaces/naming.ts index 3c05092640..5bb9f80a78 100644 --- a/apps/sim/lib/workspaces/naming.ts +++ b/apps/sim/lib/workspaces/naming.ts @@ -2,6 +2,7 @@ * Utility functions for generating names for workspaces and folders */ +import { randomItem } from '@sim/utils/random' import { requestJson } from '@/lib/api/client/request' import { type FolderApi, listFoldersContract } from '@/lib/api/contracts/folders' @@ -89,7 +90,7 @@ export function generateIncrementalName( * Generates a random cosmos-themed workspace name */ export function generateWorkspaceName(): string { - return WORKSPACE_NOUNS[Math.floor(Math.random() * WORKSPACE_NOUNS.length)] + return randomItem(WORKSPACE_NOUNS) } async function fetchWorkspaceFolders(workspaceId: string): Promise { diff --git a/apps/sim/providers/anthropic/core.ts b/apps/sim/providers/anthropic/core.ts index ab080bccee..864c9fc6b6 100644 --- a/apps/sim/providers/anthropic/core.ts +++ b/apps/sim/providers/anthropic/core.ts @@ -2,7 +2,7 @@ import type Anthropic from '@anthropic-ai/sdk' import { transformJSONSchema } from '@anthropic-ai/sdk/lib/transform-json-schema' import type { RawMessageStreamEvent } from '@anthropic-ai/sdk/resources/messages/messages' import type { Logger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type { BlockTokens, IterationToolCall, StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' import { @@ -596,7 +596,7 @@ export async function executeAnthropicProviderRequest( result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, @@ -1028,7 +1028,7 @@ export async function executeAnthropicProviderRequest( result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/anthropic/utils.ts b/apps/sim/providers/anthropic/utils.ts index 6ab45379f8..b9b001bb7a 100644 --- a/apps/sim/providers/anthropic/utils.ts +++ b/apps/sim/providers/anthropic/utils.ts @@ -5,6 +5,7 @@ import type { Usage, } from '@anthropic-ai/sdk/resources' import { createLogger } from '@sim/logger' +import { randomFloat } from '@sim/utils/random' import { trackForcedToolUsage } from '@/providers/utils' const logger = createLogger('AnthropicUtils') @@ -53,7 +54,7 @@ export function createReadableStreamFromAnthropicStream( } export function generateToolUseId(toolName: string): string { - return `${toolName}-${Date.now()}-${Math.random().toString(36).substring(2, 7)}` + return `${toolName}-${Date.now()}-${randomFloat().toString(36).substring(2, 7)}` } export function checkForForcedToolUsage( diff --git a/apps/sim/providers/azure-openai/index.ts b/apps/sim/providers/azure-openai/index.ts index 5f78c227ad..3ddc32f087 100644 --- a/apps/sim/providers/azure-openai/index.ts +++ b/apps/sim/providers/azure-openai/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { AzureOpenAI } from 'openai' import type { ChatCompletion, @@ -370,7 +370,7 @@ async function executeChatCompletionsRequest( result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/bedrock/index.ts b/apps/sim/providers/bedrock/index.ts index 22500b84c0..d7f8d53ce2 100644 --- a/apps/sim/providers/bedrock/index.ts +++ b/apps/sim/providers/bedrock/index.ts @@ -14,7 +14,7 @@ import { type ToolUseBlock, } from '@aws-sdk/client-bedrock-runtime' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type { IterationToolCall, StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' import { buildBedrockMessageContent } from '@/providers/attachments' @@ -591,7 +591,7 @@ export const bedrockProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/bedrock/utils.ts b/apps/sim/providers/bedrock/utils.ts index 9400d2378f..401c264c0c 100644 --- a/apps/sim/providers/bedrock/utils.ts +++ b/apps/sim/providers/bedrock/utils.ts @@ -1,5 +1,6 @@ import type { ConverseStreamOutput } from '@aws-sdk/client-bedrock-runtime' import { createLogger } from '@sim/logger' +import { randomFloat } from '@sim/utils/random' import { trackForcedToolUsage } from '@/providers/utils' const logger = createLogger('BedrockUtils') @@ -73,7 +74,7 @@ export function checkForForcedToolUsage( */ export function generateToolUseId(toolName: string): string { const timestamp = Date.now().toString(36) // Base36 timestamp (9 chars) - const random = Math.random().toString(36).substring(2, 7) // 5 random chars + const random = randomFloat().toString(36).substring(2, 7) // 5 random chars const suffix = `-${timestamp}-${random}` // ~15 chars const maxNameLength = 64 - suffix.length const truncatedName = toolName.substring(0, maxNameLength).replace(/[^a-zA-Z0-9_-]/g, '_') diff --git a/apps/sim/providers/cerebras/index.ts b/apps/sim/providers/cerebras/index.ts index 795cdaa498..70929f23bf 100644 --- a/apps/sim/providers/cerebras/index.ts +++ b/apps/sim/providers/cerebras/index.ts @@ -1,6 +1,6 @@ import { Cerebras } from '@cerebras/cerebras_cloud_sdk' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type { StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' import { formatMessagesForProvider } from '@/providers/attachments' @@ -289,7 +289,7 @@ export const cerebrasProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/deepseek/index.ts b/apps/sim/providers/deepseek/index.ts index fe06798d72..4f9c641291 100644 --- a/apps/sim/providers/deepseek/index.ts +++ b/apps/sim/providers/deepseek/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' @@ -299,7 +299,7 @@ export const deepseekProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/fireworks/index.ts b/apps/sim/providers/fireworks/index.ts index 5b01076bf0..8eb4e70e89 100644 --- a/apps/sim/providers/fireworks/index.ts +++ b/apps/sim/providers/fireworks/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions' import type { StreamingExecution } from '@/executor/types' @@ -333,7 +333,7 @@ export const fireworksProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/gemini/core.ts b/apps/sim/providers/gemini/core.ts index 6536d6336f..577b46454f 100644 --- a/apps/sim/providers/gemini/core.ts +++ b/apps/sim/providers/gemini/core.ts @@ -12,7 +12,7 @@ import { type ToolConfig, } from '@google/genai' import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type { IterationToolCall, StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' import { @@ -162,7 +162,7 @@ async function executeToolCallsBatch( args, resultContent: { error: true, - message: error instanceof Error ? error.message : 'Tool execution failed', + message: getErrorMessage(error, 'Tool execution failed'), tool: toolName, }, toolParams: {}, diff --git a/apps/sim/providers/groq/index.ts b/apps/sim/providers/groq/index.ts index 4165724bec..149a5dfb6d 100644 --- a/apps/sim/providers/groq/index.ts +++ b/apps/sim/providers/groq/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { Groq } from 'groq-sdk' import type { StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' @@ -277,7 +277,7 @@ export const groqProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/mistral/index.ts b/apps/sim/providers/mistral/index.ts index ce8959f304..1caa08bcf8 100644 --- a/apps/sim/providers/mistral/index.ts +++ b/apps/sim/providers/mistral/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions' import type { StreamingExecution } from '@/executor/types' @@ -341,7 +341,7 @@ export const mistralProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/ollama/index.ts b/apps/sim/providers/ollama/index.ts index 111956140c..085cf967c5 100644 --- a/apps/sim/providers/ollama/index.ts +++ b/apps/sim/providers/ollama/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions' import { getOllamaUrl } from '@/lib/core/utils/urls' @@ -50,7 +50,7 @@ export const ollamaProvider: ProviderConfig = { useProvidersStore.getState().setProviderModels('ollama', this.models) } catch (error) { logger.warn('Ollama model instantiation failed. The provider will be disabled.', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } }, @@ -350,7 +350,7 @@ export const ollamaProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/openai/core.ts b/apps/sim/providers/openai/core.ts index 1b2591d22e..11a465f2ab 100644 --- a/apps/sim/providers/openai/core.ts +++ b/apps/sim/providers/openai/core.ts @@ -1,5 +1,5 @@ import type { Logger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import type OpenAI from 'openai' import type { IterationToolCall, StreamingExecution } from '@/executor/types' import { MAX_TOOL_ITERATIONS } from '@/providers' @@ -499,7 +499,7 @@ export async function executeResponsesProviderRequest( result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/openrouter/index.ts b/apps/sim/providers/openrouter/index.ts index 09a8adbbdd..1f897ffbc7 100644 --- a/apps/sim/providers/openrouter/index.ts +++ b/apps/sim/providers/openrouter/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions' import type { StreamingExecution } from '@/executor/types' @@ -334,7 +334,7 @@ export const openRouterProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/registry.ts b/apps/sim/providers/registry.ts index 088686500b..8b1256c2de 100644 --- a/apps/sim/providers/registry.ts +++ b/apps/sim/providers/registry.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { anthropicProvider } from '@/providers/anthropic' import { azureAnthropicProvider } from '@/providers/azure-anthropic' import { azureOpenAIProvider } from '@/providers/azure-openai' @@ -57,7 +58,7 @@ export async function initializeProviders(): Promise { logger.info(`Initialized provider: ${id}`) } catch (error) { logger.error(`Failed to initialize ${id} provider`, { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } } diff --git a/apps/sim/providers/utils.ts b/apps/sim/providers/utils.ts index 780776aed3..a646214b14 100644 --- a/apps/sim/providers/utils.ts +++ b/apps/sim/providers/utils.ts @@ -1,4 +1,5 @@ import { createLogger, type Logger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type OpenAI from 'openai' import type { ChatCompletionChunk } from 'openai/resources/chat/completions' import type { CompletionUsage } from 'openai/resources/completions' @@ -430,11 +431,11 @@ export function extractAndParseJSON(content: string): any { contentLength: content.length, extractedLength: jsonStr.length, cleanedLength: cleaned.length, - error: innerError instanceof Error ? innerError.message : 'Unknown error', + error: getErrorMessage(innerError, 'Unknown error'), }) throw new Error( - `Failed to parse JSON after cleanup: ${innerError instanceof Error ? innerError.message : 'Unknown error'}` + `Failed to parse JSON after cleanup: ${getErrorMessage(innerError, 'Unknown error')}` ) } } diff --git a/apps/sim/providers/vllm/index.ts b/apps/sim/providers/vllm/index.ts index b182eefcd9..85756e4731 100644 --- a/apps/sim/providers/vllm/index.ts +++ b/apps/sim/providers/vllm/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions' import { env } from '@/lib/core/config/env' @@ -76,7 +76,7 @@ export const vllmProvider: ProviderConfig = { logger.info(`Discovered ${models.length} vLLM model(s):`, { models }) } catch (error) { logger.warn('vLLM model instantiation failed. The provider will be disabled.', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), }) } }, @@ -402,7 +402,7 @@ export const vllmProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/providers/xai/index.ts b/apps/sim/providers/xai/index.ts index bd6303007d..6f33bad536 100644 --- a/apps/sim/providers/xai/index.ts +++ b/apps/sim/providers/xai/index.ts @@ -1,5 +1,5 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import OpenAI from 'openai' import type { ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions' import type { StreamingExecution } from '@/executor/types' @@ -307,7 +307,7 @@ export const xAIProvider: ProviderConfig = { result: { success: false, output: undefined, - error: error instanceof Error ? error.message : 'Tool execution failed', + error: getErrorMessage(error, 'Tool execution failed'), }, startTime: toolCallStartTime, endTime: toolCallEndTime, diff --git a/apps/sim/stores/variables/store.ts b/apps/sim/stores/variables/store.ts index d3c71e2600..3b1e0e91b3 100644 --- a/apps/sim/stores/variables/store.ts +++ b/apps/sim/stores/variables/store.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import JSON5 from 'json5' import { create } from 'zustand' @@ -55,7 +56,7 @@ function validateVariable(variable: Variable): string | undefined { } return undefined } catch (e) { - return e instanceof Error ? e.message : 'Invalid format' + return getErrorMessage(e, 'Invalid format') } } diff --git a/apps/sim/stores/workflows/registry/store.ts b/apps/sim/stores/workflows/registry/store.ts index b9c099c488..9cf4519ff6 100644 --- a/apps/sim/stores/workflows/registry/store.ts +++ b/apps/sim/stores/workflows/registry/store.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { generateRandomHex } from '@sim/utils/random' import { create } from 'zustand' import { devtools } from 'zustand/middleware' import { requestJson } from '@/lib/api/client/request' @@ -25,7 +26,7 @@ const initialHydration: HydrationState = { error: null, } -const createRequestId = () => `${Date.now()}-${Math.random().toString(16).slice(2)}` +const createRequestId = () => `${Date.now()}-${generateRandomHex(8)}` function resetWorkflowStores() { useWorkflowStore.setState({ @@ -326,11 +327,11 @@ export const useWorkflowRegistry = create()( blockIdSet.forEach((blockId) => { const block = workflowStore.blocks[blockId] if (block) { - copiedBlocks[blockId] = JSON.parse(JSON.stringify(block)) + copiedBlocks[blockId] = structuredClone(block) if (activeWorkflowId) { const blockValues = subBlockStore.workflowValues[activeWorkflowId]?.[blockId] if (blockValues) { - copiedSubBlockValues[blockId] = JSON.parse(JSON.stringify(blockValues)) + copiedSubBlockValues[blockId] = structuredClone(blockValues) } } } @@ -343,14 +344,14 @@ export const useWorkflowRegistry = create()( const copiedLoops: Record = {} Object.entries(workflowStore.loops).forEach(([loopId, loop]) => { if (blockIdSet.has(loopId)) { - copiedLoops[loopId] = JSON.parse(JSON.stringify(loop)) + copiedLoops[loopId] = structuredClone(loop) } }) const copiedParallels: Record = {} Object.entries(workflowStore.parallels).forEach(([parallelId, parallel]) => { if (blockIdSet.has(parallelId)) { - copiedParallels[parallelId] = JSON.parse(JSON.stringify(parallel)) + copiedParallels[parallelId] = structuredClone(parallel) } }) diff --git a/apps/sim/stores/workflows/registry/utils.ts b/apps/sim/stores/workflows/registry/utils.ts index 8be102e4a6..e893b17afc 100644 --- a/apps/sim/stores/workflows/registry/utils.ts +++ b/apps/sim/stores/workflows/registry/utils.ts @@ -1,3 +1,5 @@ +import { randomItem } from '@sim/utils/random' + // Cosmos-themed adjectives and nouns for creative workflow names (max 9 chars each) const ADJECTIVES = [ // Light & Luminosity @@ -412,7 +414,7 @@ const NOUNS = [ * @returns A creative workflow name like "blazing-phoenix" or "crystal-dragon" */ export function generateCreativeWorkflowName(): string { - const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)] - const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)] + const adjective = randomItem(ADJECTIVES) + const noun = randomItem(NOUNS) return `${adjective.toLowerCase()}-${noun.toLowerCase()}` } diff --git a/apps/sim/stores/workflows/utils.ts b/apps/sim/stores/workflows/utils.ts index fbb97f22fc..ceee7b786e 100644 --- a/apps/sim/stores/workflows/utils.ts +++ b/apps/sim/stores/workflows/utils.ts @@ -351,7 +351,7 @@ export function regenerateWorkflowIds( blockIdMap.set(oldId, newId) const oldNormalizedName = normalizeName(block.name) nameMap.set(oldNormalizedName, oldNormalizedName) - const newBlock = { ...block, id: newId, subBlocks: JSON.parse(JSON.stringify(block.subBlocks)) } + const newBlock = { ...block, id: newId, subBlocks: structuredClone(block.subBlocks) } remapConditionIds(newBlock.subBlocks, {}, oldId, newId) newBlocks[newId] = newBlock }) @@ -521,7 +521,7 @@ export function regenerateBlockIds( id: newId, name: newName, position: newPosition, - subBlocks: JSON.parse(JSON.stringify(block.subBlocks)), + subBlocks: structuredClone(block.subBlocks), // Temporarily keep data as-is, we'll fix parentId in second pass data: block.data ? { ...block.data } : block.data, // Duplicated blocks are always unlocked so users can edit them @@ -533,7 +533,7 @@ export function regenerateBlockIds( allBlocksForNaming[newId] = newBlock if (subBlockValues[oldId]) { - newSubBlockValues[newId] = JSON.parse(JSON.stringify(subBlockValues[oldId])) + newSubBlockValues[newId] = structuredClone(subBlockValues[oldId]) } // Remap condition/router IDs in the duplicated block diff --git a/apps/sim/stores/workflows/workflow/store.ts b/apps/sim/stores/workflows/workflow/store.ts index e6fd406b80..0e8f011ede 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -648,7 +648,7 @@ export const useWorkflowStore = create()( ...acc, [subId]: { ...subBlock, - value: JSON.parse(JSON.stringify(subBlock.value)), + value: structuredClone(subBlock.value), }, }), {} @@ -656,10 +656,8 @@ export const useWorkflowStore = create()( // Remap condition/router IDs in the duplicated subBlocks const clonedSubBlockValues = activeWorkflowId - ? JSON.parse( - JSON.stringify( - useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[id] || {} - ) + ? structuredClone( + useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[id] || {} ) : {} remapConditionIds( diff --git a/apps/sim/tools/databricks/run_job.ts b/apps/sim/tools/databricks/run_job.ts index 6117ebcb18..d473948860 100644 --- a/apps/sim/tools/databricks/run_job.ts +++ b/apps/sim/tools/databricks/run_job.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import type { DatabricksRunJobParams, DatabricksRunJobResponse } from '@/tools/databricks/types' import type { ToolConfig } from '@/tools/types' @@ -66,7 +67,7 @@ export const runJobTool: ToolConfig): Record { - return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined)) + return filterUndefined(fields) } async function emailBisonData(response: Response): Promise { diff --git a/apps/sim/tools/gmail/utils.ts b/apps/sim/tools/gmail/utils.ts index 5de6f0ae2e..a4653d3a43 100644 --- a/apps/sim/tools/gmail/utils.ts +++ b/apps/sim/tools/gmail/utils.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import { convert } from 'html-to-text' import type { GmailAttachment, @@ -292,7 +293,7 @@ export function createMessagesSummary(messages: any[]): string { * Generate a unique MIME boundary string */ function generateBoundary(): string { - return `----=_Part_${Date.now()}_${Math.random().toString(36).substring(2, 15)}` + return `----=_Part_${Date.now()}_${generateRandomString(13)}` } /** diff --git a/apps/sim/tools/google_slides/add_image.ts b/apps/sim/tools/google_slides/add_image.ts index 3722dbe0df..ff5701ed78 100644 --- a/apps/sim/tools/google_slides/add_image.ts +++ b/apps/sim/tools/google_slides/add_image.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { generateRandomString } from '@sim/utils/random' import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleSlidesAddImageTool') @@ -122,7 +123,7 @@ export const addImageTool: ToolConfig = { } // Generate a unique object ID for the new image - const imageObjectId = `image_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` + const imageObjectId = `image_${Date.now()}_${generateRandomString(7)}` // Convert points to EMU (default sizes if not specified) const widthEmu = (params.width || 300) * PT_TO_EMU diff --git a/apps/sim/tools/google_slides/add_slide.ts b/apps/sim/tools/google_slides/add_slide.ts index d5bef81c22..2fd7281dad 100644 --- a/apps/sim/tools/google_slides/add_slide.ts +++ b/apps/sim/tools/google_slides/add_slide.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { generateRandomString } from '@sim/utils/random' import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleSlidesAddSlideTool') @@ -106,7 +107,7 @@ export const addSlideTool: ToolConfig = { }, body: (params) => { // Generate a unique object ID for the new slide - const slideObjectId = `slide_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` + const slideObjectId = `slide_${Date.now()}_${generateRandomString(7)}` // Validate and normalize the layout let layout = (params.layout || 'BLANK').toUpperCase() diff --git a/apps/sim/tools/google_slides/create_shape.ts b/apps/sim/tools/google_slides/create_shape.ts index 6c063c3864..e2459bcde8 100644 --- a/apps/sim/tools/google_slides/create_shape.ts +++ b/apps/sim/tools/google_slides/create_shape.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { generateRandomString } from '@sim/utils/random' import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleSlidesCreateShapeTool') @@ -271,7 +272,7 @@ export const createShapeTool: ToolConfig } // Generate a unique object ID for the new shape - const shapeObjectId = `shape_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` + const shapeObjectId = `shape_${Date.now()}_${generateRandomString(7)}` // Convert points to EMU const widthEmu = (params.width || 200) * PT_TO_EMU diff --git a/apps/sim/tools/google_slides/create_table.ts b/apps/sim/tools/google_slides/create_table.ts index f6f144af8a..143749c640 100644 --- a/apps/sim/tools/google_slides/create_table.ts +++ b/apps/sim/tools/google_slides/create_table.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { generateRandomString } from '@sim/utils/random' import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleSlidesCreateTableTool') @@ -135,7 +136,7 @@ export const createTableTool: ToolConfig } // Generate a unique object ID for the new table - const tableObjectId = `table_${Date.now()}_${Math.random().toString(36).substring(2, 9)}` + const tableObjectId = `table_${Date.now()}_${generateRandomString(7)}` // Convert points to EMU const widthEmu = (params.width || 400) * PT_TO_EMU diff --git a/apps/sim/tools/index.ts b/apps/sim/tools/index.ts index f19ba67626..090b30c628 100644 --- a/apps/sim/tools/index.ts +++ b/apps/sim/tools/index.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' -import { toError } from '@sim/utils/errors' +import { getErrorMessage, toError } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' +import { randomFloat } from '@sim/utils/random' import { getBYOKKey } from '@/lib/api-key/byok' import { generateInternalToken } from '@/lib/auth/internal' import { isHosted } from '@/lib/core/config/feature-flags' @@ -1273,7 +1274,7 @@ function isRetryableFailure(error: unknown, status?: number): boolean { function calculateBackoff(attempt: number, initialDelayMs: number, maxDelayMs: number): number { const base = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs) - return Math.round(base / 2 + Math.random() * (base / 2)) + return Math.round(base / 2 + randomFloat() * (base / 2)) } function parseRetryAfterHeader(header: string | null): number { @@ -1890,8 +1891,7 @@ async function executeMcpTool( logger.error(`[${actualRequestId}] Error executing MCP tool ${toolId}:`, error) - const errorMessage = - error instanceof Error ? error.message : `Failed to execute MCP tool ${toolId}` + const errorMessage = getErrorMessage(error, `Failed to execute MCP tool ${toolId}`) return { success: false, diff --git a/apps/sim/tools/langsmith/create_runs_batch.ts b/apps/sim/tools/langsmith/create_runs_batch.ts index 6dbeb56992..123ca34bd4 100644 --- a/apps/sim/tools/langsmith/create_runs_batch.ts +++ b/apps/sim/tools/langsmith/create_runs_batch.ts @@ -1,3 +1,4 @@ +import { filterUndefined } from '@sim/utils/object' import type { LangsmithCreateRunsBatchParams, LangsmithCreateRunsBatchResponse, @@ -51,7 +52,7 @@ export const langsmithCreateRunsBatchTool: ToolConfig< : undefined, } - return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== undefined)) + return filterUndefined(payload) }, }, transformResponse: async (response, params) => { diff --git a/apps/sim/tools/linkedin/share_post.ts b/apps/sim/tools/linkedin/share_post.ts index ed3bd8d198..d46d17b5a7 100644 --- a/apps/sim/tools/linkedin/share_post.ts +++ b/apps/sim/tools/linkedin/share_post.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import type { LinkedInProfileOutput, ProfileIdExtractor, @@ -131,7 +132,7 @@ export const linkedInSharePostTool: ToolConfig { if (item.b64_json) { diff --git a/apps/sim/tools/parallel/deep_research.ts b/apps/sim/tools/parallel/deep_research.ts index c608c45737..cd7dac4b9e 100644 --- a/apps/sim/tools/parallel/deep_research.ts +++ b/apps/sim/tools/parallel/deep_research.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { PlatformEvents } from '@/lib/core/telemetry' import type { ParallelDeepResearchParams } from '@/tools/parallel/types' import type { ToolConfig, ToolResponse } from '@/tools/types' @@ -202,7 +203,7 @@ export const deepResearchTool: ToolConfig = contentType, parseError: toError(parseError).message, }) - throw new Error( - `Invalid JSON response: ${parseError instanceof Error ? parseError.message : 'Parse error'}` - ) + throw new Error(`Invalid JSON response: ${getErrorMessage(parseError, 'Parse error')}`) } } diff --git a/apps/sim/tools/trello/add_comment.ts b/apps/sim/tools/trello/add_comment.ts index 2fa35b5391..fba9e542e1 100644 --- a/apps/sim/tools/trello/add_comment.ts +++ b/apps/sim/tools/trello/add_comment.ts @@ -1,3 +1,4 @@ +import { getErrorMessage } from '@sim/utils/errors' import { env } from '@/lib/core/config/env' import { extractTrelloErrorMessage, @@ -91,7 +92,7 @@ export const trelloAddCommentTool: ToolConfig ({ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v), })) +import { sleep } from '@sim/utils/helpers' import { AuditAction, AuditResourceType, recordAudit } from './index' -const flush = () => new Promise((resolve) => setTimeout(resolve, 10)) +const flush = () => sleep(10) describe('AuditAction', () => { it('contains all expected action categories', () => { diff --git a/packages/db/scripts/backfill-api-key-hash.ts b/packages/db/scripts/backfill-api-key-hash.ts index 0d6640d9da..8fdfe7fbf9 100644 --- a/packages/db/scripts/backfill-api-key-hash.ts +++ b/packages/db/scripts/backfill-api-key-hash.ts @@ -27,6 +27,7 @@ */ import { createCipheriv, createDecipheriv, createHash } from 'crypto' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq, isNull, sql } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/postgres-js' import postgres from 'postgres' @@ -171,9 +172,7 @@ export async function runBackfill(): Promise { stats.updated += 1 } catch (error) { stats.failed += 1 - console.error( - `Failed to backfill api_key id=${row.id}: ${error instanceof Error ? error.message : String(error)}` - ) + console.error(`Failed to backfill api_key id=${row.id}: ${getErrorMessage(error)}`) } }) ) @@ -225,7 +224,7 @@ if ((import.meta as { main?: boolean }).main) { try { await runBackfill() } catch (error) { - console.error('Backfill aborted:', error instanceof Error ? error.message : error) + console.error('Backfill aborted:', getErrorMessage(error)) process.exitCode = 1 } } diff --git a/packages/db/scripts/deregister-sso-provider.ts b/packages/db/scripts/deregister-sso-provider.ts index a079746d84..41df8750aa 100644 --- a/packages/db/scripts/deregister-sso-provider.ts +++ b/packages/db/scripts/deregister-sso-provider.ts @@ -13,6 +13,7 @@ * SSO_PROVIDER_ID=provider-id (optional, if not provided will remove all providers for user) */ +import { getErrorMessage } from '@sim/utils/errors' import { and, eq } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/postgres-js' import postgres from 'postgres' @@ -137,7 +138,7 @@ async function deregisterSSOProvider(): Promise { return true } catch (error) { logger.error('❌ Failed to deregister SSO provider:', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), stack: error instanceof Error ? error.stack : undefined, }) return false diff --git a/packages/db/scripts/migrate-block-api-keys-to-byok.ts b/packages/db/scripts/migrate-block-api-keys-to-byok.ts index 25b3fb3df3..1dc83f876e 100644 --- a/packages/db/scripts/migrate-block-api-keys-to-byok.ts +++ b/packages/db/scripts/migrate-block-api-keys-to-byok.ts @@ -22,6 +22,7 @@ import { createCipheriv, createDecipheriv, randomBytes } from 'crypto' import { appendFileSync, readFileSync, writeFileSync } from 'fs' import { resolve } from 'path' +import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { eq, sql } from 'drizzle-orm' import { index, json, jsonb, pgTable, text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core' @@ -325,10 +326,6 @@ function mergeStats(target: MigrationStats, source: MigrationStats) { target.envVarFailures += source.envVarFailures } -function sleep(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)) -} - async function resolveKey( ref: RawKeyRef, env: EnvLookup, diff --git a/packages/db/scripts/register-sso-provider.ts b/packages/db/scripts/register-sso-provider.ts index 293d6f545c..5efd45742b 100644 --- a/packages/db/scripts/register-sso-provider.ts +++ b/packages/db/scripts/register-sso-provider.ts @@ -33,6 +33,7 @@ * SSO_SAML_WANT_ASSERTIONS_SIGNED=true (optional, defaults to false) */ +import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/postgres-js' @@ -507,7 +508,7 @@ async function registerSSOProvider(): Promise { }) } catch (error) { logger.error('Error fetching OIDC discovery document', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), discoveryUrl, }) logger.error( @@ -649,7 +650,7 @@ async function registerSSOProvider(): Promise { return true } catch (error) { logger.error('❌ Failed to register SSO provider:', { - error: error instanceof Error ? error.message : 'Unknown error', + error: getErrorMessage(error, 'Unknown error'), errorType: typeof error, errorDetails: JSON.stringify(error), stack: error instanceof Error ? error.stack : undefined, diff --git a/packages/db/scripts/seed-stress-test-users.ts b/packages/db/scripts/seed-stress-test-users.ts index dcab3170e2..10b144ccae 100644 --- a/packages/db/scripts/seed-stress-test-users.ts +++ b/packages/db/scripts/seed-stress-test-users.ts @@ -7,6 +7,7 @@ */ import { generateId } from '@sim/utils/id' +import { randomFloat, randomInt, randomItem } from '@sim/utils/random' import { eq, type InferInsertModel } from 'drizzle-orm' import { db, userTableDefinitions, userTableRows } from '../index' @@ -92,16 +93,8 @@ const lastNames = [ 'Martin', ] -function randomItem(arr: T[]): T { - return arr[Math.floor(Math.random() * arr.length)] -} - -function randomInt(min: number, max: number): number { - return Math.floor(Math.random() * (max - min + 1)) + min -} - function randomDate(start: Date, end: Date): string { - const date = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())) + const date = new Date(start.getTime() + randomFloat() * (end.getTime() - start.getTime())) return date.toISOString().split('T')[0] } @@ -116,7 +109,7 @@ function generateUserRow(index: number): UserRow { age: randomInt(22, 65), department: randomItem(departments), salary: randomInt(40000, 200000), - active: Math.random() > 0.1, // 90% active + active: randomFloat() > 0.1, // 90% active hire_date: randomDate(new Date('2015-01-01'), new Date('2024-12-31')), country: randomItem(countries), } diff --git a/packages/testing/src/builders/execution.builder.ts b/packages/testing/src/builders/execution.builder.ts index 138630fc62..fed0dc7966 100644 --- a/packages/testing/src/builders/execution.builder.ts +++ b/packages/testing/src/builders/execution.builder.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import type { ExecutionContext } from '../types' /** @@ -18,7 +19,7 @@ import type { ExecutionContext } from '../types' */ export class ExecutionContextBuilder { private workflowId = 'test-workflow' - private executionId = `exec-${Math.random().toString(36).substring(2, 10)}` + private executionId = `exec-${generateRandomString(8)}` private blockStates = new Map() private executedBlocks = new Set() private blockLogs: any[] = [] diff --git a/packages/testing/src/builders/workflow.builder.ts b/packages/testing/src/builders/workflow.builder.ts index ce074d8f06..39075e4695 100644 --- a/packages/testing/src/builders/workflow.builder.ts +++ b/packages/testing/src/builders/workflow.builder.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import { createAgentBlock, createBlock, @@ -229,7 +230,7 @@ export class WorkflowBuilder { value: any ): this { this.variables?.push({ - id: `var-${Math.random().toString(36).substring(2, 8)}`, + id: `var-${generateRandomString(6)}`, name, type, value, diff --git a/packages/testing/src/factories/block.factory.ts b/packages/testing/src/factories/block.factory.ts index 1020f651f8..57b947afb1 100644 --- a/packages/testing/src/factories/block.factory.ts +++ b/packages/testing/src/factories/block.factory.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import type { BlockData, BlockOutput, Position } from '../types' /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -28,7 +29,7 @@ export interface BlockFactoryOptions { * Generates a unique block ID. */ function generateBlockId(prefix = 'block'): string { - return `${prefix}-${Math.random().toString(36).substring(2, 10)}` + return `${prefix}-${generateRandomString(8)}` } /** diff --git a/packages/testing/src/factories/dag.factory.ts b/packages/testing/src/factories/dag.factory.ts index 2b3bdd74cf..d9eb938c54 100644 --- a/packages/testing/src/factories/dag.factory.ts +++ b/packages/testing/src/factories/dag.factory.ts @@ -3,6 +3,7 @@ * These are used in executor tests for DAG construction and edge management */ +import { generateRandomString } from '@sim/utils/random' import { createSerializedBlock, type SerializedBlock } from './serialized-block.factory' /** @@ -62,7 +63,7 @@ export interface DAGNodeFactoryOptions { * ``` */ export function createDAGNode(options: DAGNodeFactoryOptions = {}): DAGNode { - const id = options.id ?? `node-${Math.random().toString(36).substring(2, 8)}` + const id = options.id ?? `node-${generateRandomString(6)}` const block = options.block ?? createSerializedBlock({ diff --git a/packages/testing/src/factories/edge.factory.ts b/packages/testing/src/factories/edge.factory.ts index 65e6e69f08..1bdcf37749 100644 --- a/packages/testing/src/factories/edge.factory.ts +++ b/packages/testing/src/factories/edge.factory.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { generateRandomString } from '@sim/utils/random' + /** * Options for creating a mock edge. */ @@ -17,7 +19,7 @@ export interface EdgeFactoryOptions { * Generates an edge ID from source and target. */ function generateEdgeId(source: string, target: string): string { - return `${source}-${target}-${Math.random().toString(36).substring(2, 6)}` + return `${source}-${target}-${generateRandomString(4)}` } /** diff --git a/packages/testing/src/factories/execution.factory.ts b/packages/testing/src/factories/execution.factory.ts index 38df3cb716..4a80d37431 100644 --- a/packages/testing/src/factories/execution.factory.ts +++ b/packages/testing/src/factories/execution.factory.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import type { ExecutionContext } from '../types' /** @@ -38,7 +39,7 @@ export function createExecutionContext( ): ExecutionContext { return { workflowId: options.workflowId ?? 'test-workflow', - executionId: options.executionId ?? `exec-${Math.random().toString(36).substring(2, 10)}`, + executionId: options.executionId ?? `exec-${generateRandomString(8)}`, blockStates: options.blockStates ?? new Map(), executedBlocks: options.executedBlocks ?? new Set(), blockLogs: options.blockLogs ?? [], diff --git a/packages/testing/src/factories/tool-responses.factory.ts b/packages/testing/src/factories/tool-responses.factory.ts index 7440c9c6d6..a41e6575f8 100644 --- a/packages/testing/src/factories/tool-responses.factory.ts +++ b/packages/testing/src/factories/tool-responses.factory.ts @@ -4,6 +4,8 @@ * This file contains mock data samples to be used in tool unit tests. */ +import { randomFloat } from '@sim/utils/random' + /** * HTTP Request mock responses for different scenarios. */ @@ -147,7 +149,7 @@ export const mockPineconeResponses = { embedding: { embedding: Array(1536) .fill(0) - .map(() => Math.random() * 2 - 1), + .map(() => randomFloat() * 2 - 1), metadata: { text: 'Sample text for embedding', id: 'embed-123' }, }, searchResults: { diff --git a/packages/testing/src/factories/user.factory.ts b/packages/testing/src/factories/user.factory.ts index c98babab91..662c0ac477 100644 --- a/packages/testing/src/factories/user.factory.ts +++ b/packages/testing/src/factories/user.factory.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import type { User, Workflow, WorkflowState, Workspace } from '../types' import { createWorkflowState } from './workflow.factory' @@ -20,7 +21,7 @@ export interface UserFactoryOptions { * ``` */ export function createUser(options: UserFactoryOptions = {}): User { - const id = options.id ?? `user-${Math.random().toString(36).substring(2, 10)}` + const id = options.id ?? `user-${generateRandomString(8)}` return { id, email: options.email ?? `${id}@test.example.com`, @@ -51,9 +52,9 @@ export interface WorkspaceFactoryOptions { export function createWorkspace(options: WorkspaceFactoryOptions = {}): Workspace { const now = new Date() return { - id: options.id ?? `ws-${Math.random().toString(36).substring(2, 10)}`, + id: options.id ?? `ws-${generateRandomString(8)}`, name: options.name ?? 'Test Workspace', - ownerId: options.ownerId ?? `user-${Math.random().toString(36).substring(2, 10)}`, + ownerId: options.ownerId ?? `user-${generateRandomString(8)}`, createdAt: options.createdAt ?? now, updatedAt: options.updatedAt ?? now, } @@ -83,9 +84,9 @@ export interface WorkflowObjectFactoryOptions { export function createWorkflow(options: WorkflowObjectFactoryOptions = {}): Workflow { const now = new Date() return { - id: options.id ?? `wf-${Math.random().toString(36).substring(2, 10)}`, + id: options.id ?? `wf-${generateRandomString(8)}`, name: options.name ?? 'Test Workflow', - workspaceId: options.workspaceId ?? `ws-${Math.random().toString(36).substring(2, 10)}`, + workspaceId: options.workspaceId ?? `ws-${generateRandomString(8)}`, state: options.state ?? createWorkflowState(), createdAt: options.createdAt ?? now, updatedAt: options.updatedAt ?? now, diff --git a/packages/testing/src/mocks/socket.mock.ts b/packages/testing/src/mocks/socket.mock.ts index 249b5ac8fe..4a142669be 100644 --- a/packages/testing/src/mocks/socket.mock.ts +++ b/packages/testing/src/mocks/socket.mock.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import { type Mock, vi } from 'vitest' /** @@ -34,7 +35,7 @@ export function createMockSocket(): IMockSocket { const eventHandlers: Record any)[]> = {} const socket = { - id: `socket-${Math.random().toString(36).substring(2, 10)}`, + id: `socket-${generateRandomString(8)}`, connected: true, disconnected: false, diff --git a/packages/testing/src/mocks/terminal-console.mock.ts b/packages/testing/src/mocks/terminal-console.mock.ts index b0684c0212..f695396971 100644 --- a/packages/testing/src/mocks/terminal-console.mock.ts +++ b/packages/testing/src/mocks/terminal-console.mock.ts @@ -1,3 +1,4 @@ +import { generateRandomString } from '@sim/utils/random' import { vi } from 'vitest' interface ConsoleEntryLike { @@ -16,7 +17,7 @@ const entriesByWorkflow: Record = {} const mockGetWorkflowEntries = vi.fn((workflowId: string) => entriesByWorkflow[workflowId] ?? []) const mockAddConsole = vi.fn((entry: ConsoleEntryLike) => { - const stored = { ...entry, id: entry.id ?? `mock-${Math.random().toString(36).slice(2)}` } + const stored = { ...entry, id: entry.id ?? `mock-${generateRandomString(16)}` } if (!entriesByWorkflow[entry.workflowId]) entriesByWorkflow[entry.workflowId] = [] entriesByWorkflow[entry.workflowId].push(stored) return stored diff --git a/packages/ts-sdk/src/index.ts b/packages/ts-sdk/src/index.ts index ffed7ca1e7..bde2321b68 100644 --- a/packages/ts-sdk/src/index.ts +++ b/packages/ts-sdk/src/index.ts @@ -434,6 +434,7 @@ export class SimStudioClient { ? this.rateLimitInfo.retryAfter : Math.min(delay, maxDelay) + // standalone package — cannot depend on @sim/utils const jitter = waitTime * (0.75 + Math.random() * 0.5) await new Promise((resolve) => setTimeout(resolve, jitter)) diff --git a/packages/utils/package.json b/packages/utils/package.json index 013c600c56..d02e9bd46a 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -14,6 +14,10 @@ "types": "./src/id.ts", "default": "./src/id.ts" }, + "./random": { + "types": "./src/random.ts", + "default": "./src/random.ts" + }, "./errors": { "types": "./src/errors.ts", "default": "./src/errors.ts" @@ -25,6 +29,18 @@ "./formatting": { "types": "./src/formatting.ts", "default": "./src/formatting.ts" + }, + "./object": { + "types": "./src/object.ts", + "default": "./src/object.ts" + }, + "./string": { + "types": "./src/string.ts", + "default": "./src/string.ts" + }, + "./retry": { + "types": "./src/retry.ts", + "default": "./src/retry.ts" } }, "scripts": { diff --git a/packages/utils/src/errors.ts b/packages/utils/src/errors.ts index 5bfc309c1c..48fcee083c 100644 --- a/packages/utils/src/errors.ts +++ b/packages/utils/src/errors.ts @@ -8,6 +8,20 @@ export function toError(value: unknown): Error { return new Error(String(value)) } +/** + * Extracts a string message from an unknown caught value. + * Use instead of `e instanceof Error ? e.message : 'fallback'` in catch clauses. + * + * - Error instance → `error.message` + * - Non-empty string → the string itself (handles `throw 'msg'` patterns) + * - Otherwise → `fallback` if provided, or `String(value)` + */ +export function getErrorMessage(value: unknown, fallback?: string): string { + if (value instanceof Error) return value.message + if (typeof value === 'string' && value.length > 0) return value + return fallback ?? String(value) +} + /** * Returns PostgreSQL error code (e.g. `23505` for unique_violation) when present on a thrown value. * Normalizes common Drizzle / `postgres` driver shapes and walks `cause` chains. diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 83147e181a..d40a413271 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,4 @@ -export { getPostgresErrorCode, toError } from './errors.js' +export { getErrorMessage, getPostgresErrorCode, toError } from './errors.js' export { formatAbsoluteDate, formatCompactTimestamp, @@ -12,3 +12,16 @@ export { } from './formatting.js' export { noop, sleep } from './helpers.js' export { generateId, generateShortId, isValidUuid } from './id.js' +export { filterUndefined, omit } from './object.js' +export { + generateRandomBytes, + generateRandomHex, + generateRandomString, + LOWERCASE_ALPHANUMERIC_ALPHABET, + randomFloat, + randomInt, + randomItem, +} from './random.js' +export type { BackoffOptions } from './retry.js' +export { backoffWithJitter, parseRetryAfter } from './retry.js' +export { truncate } from './string.js' diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts new file mode 100644 index 0000000000..b16f0f5c95 --- /dev/null +++ b/packages/utils/src/object.ts @@ -0,0 +1,24 @@ +/** + * Returns a new object with the given keys removed. + * + * @example + * omit({ a: 1, b: 2, c: 3 }, ['b', 'c']) // { a: 1 } + */ +export function omit(obj: T, keys: K[]): Omit { + const result = { ...obj } + for (const key of keys) { + delete result[key] + } + return result +} + +/** + * Returns a shallow copy of `obj` with all `undefined`-valued keys removed. + * Useful for building query-param or request-body objects where undefined + * fields should not be serialized. + * + * Replaces the common `Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined))` pattern. + */ +export function filterUndefined>(obj: T): Partial { + return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as Partial +} diff --git a/packages/utils/src/random.ts b/packages/utils/src/random.ts new file mode 100644 index 0000000000..da00e83f41 --- /dev/null +++ b/packages/utils/src/random.ts @@ -0,0 +1,78 @@ +/** + * Cryptographically secure random utilities built on `crypto.getRandomValues()`. + * Works in all contexts including non-secure (HTTP) browser environments. + */ + +/** Lowercase alphanumeric characters used as the default alphabet for random strings. */ +export const LOWERCASE_ALPHANUMERIC_ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789' + +const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + +/** + * Generates cryptographically secure random bytes. + * @param length - Number of bytes to generate + * @returns Uint8Array of random bytes + */ +export function generateRandomBytes(length: number): Uint8Array { + return crypto.getRandomValues(new Uint8Array(length)) +} + +/** + * Generates a cryptographically secure random hex string. + * @param length - Number of hex characters (default: 16) + * @returns Lowercase hex string of the given length + */ +export function generateRandomHex(length = 16): string { + const bytes = generateRandomBytes(Math.ceil(length / 2)) + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') + .slice(0, length) +} + +/** + * Generates a cryptographically secure random alphanumeric string. + * @param length - Number of characters (default: 16) + * @returns Random string composed of A-Z, a-z, 0-9 + */ +export function generateRandomString(length = 16): string { + const bytes = generateRandomBytes(length) + return Array.from(bytes) + .map((b) => CHARS[b % CHARS.length]) + .join('') +} + +/** + * Returns a cryptographically secure random float in [0, 1). + * Drop-in replacement for `Math.random()`. + */ +export function randomFloat(): number { + const [value] = crypto.getRandomValues(new Uint32Array(1)) + return value / 0x100000000 +} + +/** + * Returns a cryptographically secure random integer in [min, max). + * Uses rejection sampling for uniform distribution (no modulo bias). + * @param min - Inclusive lower bound + * @param max - Exclusive upper bound + */ +export function randomInt(min: number, max: number): number { + const range = max - min + if (range <= 0) throw new RangeError(`randomInt: max (${max}) must be greater than min (${min})`) + const threshold = (0x100000000 - (0x100000000 % range)) >>> 0 + let value: number + do { + ;[value] = crypto.getRandomValues(new Uint32Array(1)) + } while (value >= threshold) + return min + (value % range) +} + +/** + * Returns a uniformly random element from a non-empty array. + * @param items - Array to sample from (must have at least one element) + */ +export function randomItem(items: readonly T[]): T { + if (items.length === 0) throw new RangeError('randomItem: array must not be empty') + return items[randomInt(0, items.length)] +} diff --git a/packages/utils/src/retry.ts b/packages/utils/src/retry.ts new file mode 100644 index 0000000000..cc32d8c83e --- /dev/null +++ b/packages/utils/src/retry.ts @@ -0,0 +1,59 @@ +/** Default retry pacing: 500 ms floor, 30 s ceiling. */ +const DEFAULT_BACKOFF_BASE_MS = 500 +const DEFAULT_BACKOFF_MAX_MS = 30_000 + +export interface BackoffOptions { + baseMs?: number + maxMs?: number +} + +/** + * Computes the next delay for a retry loop. + * + * When `retryAfterMs` is non-null (from a `Retry-After` response header), the + * value is clamped to `[baseMs, maxMs]` so a malformed `Retry-After: 0` cannot + * pin the loop into a tight retry. Otherwise returns exponential backoff with + * ±20% jitter to avoid thundering-herd alignment across concurrent callers. + * Attempt is 1-indexed. + */ +export function backoffWithJitter( + attempt: number, + retryAfterMs: number | null, + options: BackoffOptions = {} +): number { + const baseMs = options.baseMs ?? DEFAULT_BACKOFF_BASE_MS + const maxMs = options.maxMs ?? DEFAULT_BACKOFF_MAX_MS + if (retryAfterMs !== null) { + return Math.min(Math.max(retryAfterMs, baseMs), maxMs) + } + const exponential = Math.min(baseMs * 2 ** (attempt - 1), maxMs) + // Inline crypto float to avoid cross-file imports within the package (Turbopack limitation) + const jitter = crypto.getRandomValues(new Uint32Array(1))[0] / 0x100000000 + return exponential * (0.8 + jitter * 0.4) +} + +/** Maximum `Retry-After` value honored: 30 s. Prevents a misconfigured upstream from stalling callers. */ +const RETRY_AFTER_MAX_MS = 30_000 + +/** + * Parses an HTTP `Retry-After` header (either delta-seconds or an HTTP-date) + * into a millisecond delay, capped at 30 s. + * Returns `null` when the header is absent or unparseable so callers can fall + * back to their own backoff. + */ +export function parseRetryAfter(header: string | null): number | null { + if (!header) return null + const trimmed = header.trim() + if (trimmed.length === 0) return null + const seconds = Number(trimmed) + if (Number.isFinite(seconds) && seconds >= 0) { + return Math.min(Math.floor(seconds * 1000), RETRY_AFTER_MAX_MS) + } + const dateMs = Date.parse(trimmed) + if (!Number.isNaN(dateMs)) { + const delta = dateMs - Date.now() + if (delta <= 0) return 0 + return Math.min(delta, RETRY_AFTER_MAX_MS) + } + return null +} diff --git a/packages/utils/src/string.ts b/packages/utils/src/string.ts new file mode 100644 index 0000000000..060419841f --- /dev/null +++ b/packages/utils/src/string.ts @@ -0,0 +1,13 @@ +/** + * Truncates `str` if it exceeds `sliceLength` characters, appending `suffix`. + * The total output length when truncated is `sliceLength + suffix.length`. + * Defaults suffix to `'...'`. + * + * @example + * truncate('hello world', 8) // 'hello wo...' (11 chars) + * truncate('hello world', 8, ' …') // 'hello wo …' + * truncate('hi', 10) // 'hi' + */ +export function truncate(str: string, sliceLength: number, suffix = '...'): string { + return str.length > sliceLength ? str.slice(0, sliceLength) + suffix : str +} diff --git a/scripts/check-utils-enforcement.ts b/scripts/check-utils-enforcement.ts new file mode 100644 index 0000000000..22565e4c78 --- /dev/null +++ b/scripts/check-utils-enforcement.ts @@ -0,0 +1,154 @@ +#!/usr/bin/env bun +/** + * Enforces use of shared @sim/utils helpers over inline implementations. + * + * Biome's noRestrictedImports covers import-based bans (nanoid, uuid, crypto named imports). + * This script catches patterns that static import analysis misses — global property access, + * inline idioms, and reimplemented helpers that should live in @sim/utils. + */ +import { readdir, readFile } from 'node:fs/promises' +import path from 'node:path' + +const ROOT = path.resolve(import.meta.dir, '..') + +const SCAN_DIRS = [path.join(ROOT, 'apps'), path.join(ROOT, 'packages')] + +const SKIP_DIRS = new Set(['node_modules', 'dist', '.next', '.turbo', 'coverage', 'bundles']) + +/** Files that implement the utilities themselves — allowed to use the underlying primitives. */ +const ALLOWLISTED_FILES = new Set([ + 'packages/utils/src/errors.ts', + 'packages/utils/src/helpers.ts', + 'packages/utils/src/random.ts', + 'packages/utils/src/id.ts', + 'packages/utils/src/object.ts', + 'packages/utils/src/retry.ts', + 'packages/utils/src/errors.test.ts', + 'packages/utils/src/helpers.test.ts', + 'packages/utils/src/random.test.ts', + 'packages/utils/src/id.test.ts', + 'packages/utils/src/object.test.ts', + 'packages/utils/src/retry.test.ts', + 'packages/cli/src/index.ts', + 'packages/ts-sdk/src/index.ts', + // CJS bundle — cannot use ES module imports + 'apps/sim/lib/execution/isolated-vm-worker.cjs', + // Uses crypto.getRandomValues() directly (not crypto.randomUUID) — TSDoc comment triggers false positive + 'packages/testing/src/factories/id.ts', +]) + +const BANNED_PATTERNS: Array<{ + pattern: RegExp + description: string + suggestion: string +}> = [ + // Randomness / ID generation — global property access that import bans miss + { + pattern: /\bMath\.random\s*\(/g, + description: 'Math.random()', + suggestion: 'randomInt / randomFloat / randomItem from @sim/utils/random', + }, + { + pattern: /\bcrypto\.randomUUID\s*\(/g, + description: 'crypto.randomUUID()', + suggestion: 'generateId() or generateShortId() from @sim/utils/id', + }, + { + pattern: /\bcrypto\.randomBytes\s*\(/g, + description: 'crypto.randomBytes()', + suggestion: 'generateRandomBytes() or generateRandomHex() from @sim/utils/random', + }, + // Deep clone idiom + { + pattern: /JSON\.parse\s*\(\s*JSON\.stringify\s*\(/g, + description: 'JSON.parse(JSON.stringify(...))', + suggestion: 'structuredClone() — built-in, no import needed', + }, + // Inline error message extraction (excludes null/undefined/false fallbacks — those have different semantics) + { + pattern: /instanceof Error\s*\?\s*\w+\.message\s*:\s*(?!\s*null\b|\s*undefined\b|\s*false\b)./g, + description: 'e instanceof Error ? e.message : fallback', + suggestion: 'getErrorMessage(e, fallback?) from @sim/utils/errors', + }, + // Inline sleep + { + pattern: /new Promise\s*[(<]\s*(?:resolve|\(resolve\))\s*=>\s*setTimeout\s*\(\s*resolve/g, + description: 'new Promise(resolve => setTimeout(resolve, ms))', + suggestion: 'sleep(ms) from @sim/utils/helpers', + }, +] + +async function walk(dir: string, results: string[] = []): Promise { + let entries + try { + entries = await readdir(dir, { withFileTypes: true }) + } catch { + return results + } + for (const entry of entries) { + if (SKIP_DIRS.has(entry.name)) continue + const full = path.join(dir, entry.name) + if (entry.isDirectory()) { + await walk(full, results) + } else if (/\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/.test(entry.name)) { + results.push(full) + } + } + return results +} + +interface Violation { + file: string + line: number + description: string + suggestion: string + snippet: string +} + +async function main() { + const allFiles: string[] = [] + for (const dir of SCAN_DIRS) { + await walk(dir, allFiles) + } + + const violations: Violation[] = [] + + for (const file of allFiles) { + const rel = path.relative(ROOT, file) + if (ALLOWLISTED_FILES.has(rel)) continue + + const content = await readFile(file, 'utf8') + const lines = content.split('\n') + + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + for (const { pattern, description, suggestion } of BANNED_PATTERNS) { + pattern.lastIndex = 0 + if (pattern.test(line)) { + violations.push({ + file: rel, + line: i + 1, + description, + suggestion, + snippet: line.trim(), + }) + } + } + } + } + + if (violations.length === 0) { + console.log('✓ No banned patterns found.') + process.exit(0) + } + + console.error(`\nFound ${violations.length} banned pattern(s):\n`) + for (const v of violations) { + console.error(` ${v.file}:${v.line}`) + console.error(` ✗ ${v.description} → use ${v.suggestion}`) + console.error(` ${v.snippet}\n`) + } + process.exit(1) +} + +main()