From bac222440f03271179d7d201bb2a93cc60de2afc Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 15 May 2026 16:50:50 -0700 Subject: [PATCH 1/6] 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 --- .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 | 2 +- .../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 | 2 +- .../app/api/tools/ssh/execute-script/route.ts | 2 +- .../api/tools/ssh/get-system-info/route.ts | 2 +- .../app/api/tools/ssh/list-directory/route.ts | 2 +- .../app/api/tools/ssh/move-rename/route.ts | 2 +- .../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 | 3 +- 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 | 8 +- .../settings/components/mcp/mcp.tsx | 3 +- .../components/secrets/secrets-manager.tsx | 25 +-- .../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 | 7 +- .../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 | 2 +- apps/sim/executor/utils/json.ts | 2 +- apps/sim/hooks/queries/mcp.ts | 5 +- apps/sim/hooks/queries/providers.ts | 2 +- apps/sim/hooks/use-execution-stream.ts | 2 +- apps/sim/instrumentation-client.ts | 3 +- .../api/contracts/tools/databases/shared.ts | 3 +- apps/sim/lib/billing/client/upgrade.ts | 4 +- apps/sim/lib/billing/credits/purchase.ts | 2 +- .../lib/billing/organizations/membership.ts | 2 +- apps/sim/lib/billing/stripe-payment-method.ts | 2 +- .../lib/billing/webhooks/outbox-handlers.ts | 2 +- 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 | 4 +- .../copilot/request/handlers/handlers.test.ts | 29 ++-- apps/sim/lib/copilot/request/handlers/tool.ts | 4 +- .../lib/copilot/request/lifecycle/headless.ts | 2 +- .../lib/copilot/request/lifecycle/start.ts | 12 +- .../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 | 3 +- 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 | 5 +- apps/sim/lib/core/security/encryption.ts | 3 +- apps/sim/lib/core/security/redaction.test.ts | 3 +- 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 | 2 +- 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 | 2 +- apps/sim/lib/messaging/sms/service.ts | 4 +- apps/sim/lib/mothership/inbox/executor.ts | 10 +- apps/sim/lib/mothership/inbox/response.ts | 2 +- 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 | 6 +- apps/sim/lib/workflows/autolayout/targeted.ts | 3 +- apps/sim/lib/workflows/colors.ts | 4 +- .../workflows/comparison/resolve-values.ts | 5 +- .../credentials/credential-extractor.ts | 3 +- 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 | 5 +- .../lib/workflows/persistence/utils.test.ts | 3 +- 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 | 2 +- apps/sim/providers/fireworks/index.ts | 2 +- apps/sim/providers/gemini/core.ts | 2 +- apps/sim/providers/groq/index.ts | 2 +- apps/sim/providers/mistral/index.ts | 2 +- apps/sim/providers/ollama/index.ts | 4 +- apps/sim/providers/openai/core.ts | 2 +- apps/sim/providers/openrouter/index.ts | 2 +- apps/sim/providers/registry.ts | 3 +- apps/sim/providers/utils.ts | 5 +- apps/sim/providers/vllm/index.ts | 4 +- apps/sim/providers/xai/index.ts | 2 +- apps/sim/stores/variables/store.ts | 3 +- apps/sim/stores/workflows/registry/store.ts | 12 +- apps/sim/stores/workflows/registry/utils.ts | 6 +- apps/sim/stores/workflows/utils.ts | 7 +- apps/sim/stores/workflows/workflow/store.ts | 9 +- 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 | 6 +- 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 | 3 +- 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 | 4 +- 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 +- 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 | 36 ++++ packages/utils/src/random.ts | 78 +++++++++ packages/utils/src/retry.ts | 59 +++++++ packages/utils/src/string.ts | 12 ++ scripts/check-utils-enforcement.ts | 154 ++++++++++++++++++ 533 files changed, 1658 insertions(+), 1041 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 b5bc94ec1b2..4da51a0c70d 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'` +- `deepClone(value)` from `@sim/utils/object` — structural clone. 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 { deepClone, 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 = deepClone(obj) +const filtered = filterUndefined(obj) ``` ## Package Manager diff --git a/CLAUDE.md b/CLAUDE.md index 9807d221ca6..9a8e18cc95a 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'` + - `deepClone(value)` from `@sim/utils/object` — structural 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 4904daccb8f..8e301acf9bc 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 1884c907812..3fa4ac0f8e7 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 4650f8487cc..a54e3eb99e2 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 5bde4c326d9..b7c7a529c80 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 c00592e0d6d..f2d6d8e6868 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 17a2dd4a0e6..fb32e2decdc 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 337120d631b..bdcf335220e 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 a8954abeb3a..2a246c7cb3e 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 7deafb3ad15..a3a022f9d82 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 802b1f0c1cc..9dcf5977a5e 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 adc517ceff9..d7d1b9cd9f3 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 a68c830f0ef..5b285a5bd28 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 15e523b26b8..33a9f40a082 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 f74a0e04094..0115be7099b 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 ca1afd1b162..ca1f8019fe7 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 29ec7c2f3bf..25416137472 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 7cc61cb6447..bd7f5465685 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 58f3435b4f9..1dd5bd98a12 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 be665c49384..6d598984225 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 85651ac89bc..e14cff30b49 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 34974280629..e69cfc5ecd1 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 993961bb29d..100b53b77a3 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 c268e297f49..ee3a5ec3e41 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 bf4d9fd8276..8612f0b1f83 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 4bcd7d01914..aa3c3ff93c3 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 8c3eda979d4..7484712978c 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 828cf83aa09..2bdd3c81d18 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 8f74418c534..5ad28b90b28 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 d1c05bbe627..982a8b36bda 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 c0592bc366b..2b232c9132f 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 42baba4c85d..0db7f37ee96 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 5d0d7ba50d3..c977a8c5edf 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 822094f32a6..128f70ae37c 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 f167d06d0b0..4bde63da800 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 92929d23f11..5dee08582a6 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 f93c0f5afe6..482ee61950b 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 1e1c0ee0bc9..c1ca7c03eb3 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 136c41e836b..f9e3c8c0c2b 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 0f170ae0fc1..aa87abbcb7d 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 bb6d036ab02..ef3c000d960 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 1c366322314..47c302ba28b 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 c44162b7e2d..11e53803b70 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 ec5cfcba94e..f0bfc2b4a45 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 b933e1a9815..6e2e167220e 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 42912ea8eff..571f4764078 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 7de0054eca5..70da8c28870 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 e7dd16ff85b..066b03480c1 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 64e2600bb94..0e73ecaaeba 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 0a14cf6fc91..df188ea652c 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 d03b0dee3bb..44dabf2404a 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 f56942b8dde..4eb3c56acf1 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 7b13c7da3e9..abf3b978387 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 053c72d9c02..899bc096adf 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 b7e61381983..2f1289db064 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 28b0a081d4b..4b9c53d1a6e 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 c025cf62f10..51602fdc937 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 df0304909a2..4d4687d0a65 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 576cd0066ba..186c8df8b90 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 73519befcd7..95ca9979054 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 efd7c7a7b73..a46a22deb57 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 0a772aa10c4..64775f37125 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 9949df87cbc..b2ff65c9723 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 926c5c12faa..d0c8a719574 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 8429a19c376..3b695de8ca0 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 290809ffdf4..b10c02872ce 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 86bbfa89c3e..da1af83142f 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 32ae35a0a27..be4932a9964 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 280775b3b41..538bcc653e6 100644 --- a/apps/sim/app/api/tools/crowdstrike/query/route.ts +++ b/apps/sim/app/api/tools/crowdstrike/query/route.ts @@ -405,7 +405,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { output: normalizeAggregatesOutput(aggregateData), }) } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error' + const message = getErrorMessage(error, 'Unknown error') 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 c94109c5928..b32e78ddd15 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 7bf95fbe1fa..05f76cef2a2 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 9a4c24096d4..5efc1b44d21 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 04c363def3b..c88878bb73b 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 2c14fcd0dc6..d9f375c819a 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 d3ee0699df4..cddd0c2d404 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 3230109e955..3e8aa3f1e97 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 33fa687ecfe..c8a5ddd9c7d 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 bdde802b929..d0bd66517c2 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 48c26786a91..c1d4d6f4e9c 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 55b2c0ace17..5947a28ed98 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 464d1e28142..08bb24f26c0 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 3280a833283..bf55d7a87cb 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 5162dcba273..ffbe255c16d 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 6b14897a31e..ae9a3120249 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 896a17a817f..f1d816328ef 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 1f0cf8324ed..2c5a2e73722 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 cfb53f06430..f95b4a9941d 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 8a5cdd91152..ed9575c9976 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 c47f4d4d6af..41da810a2f0 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 26dca5fac50..46161021ffc 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 79598baad03..ba83d8bf740 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 1b51ff59d28..ca4ab7b2599 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 dc8b3e71785..cac63c2ab92 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 c7c17009622..a9843788a65 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 64082a18402..d56f8328c1f 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 fc088a6d73a..fb16b0c363e 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 f68eeffc980..537f245217a 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 d0e6d1b6401..81818bfc98e 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 a0eaf2d1f9e..3f81f633891 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 69fc234487f..8d063b8058c 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 797d7cf11d2..9c0cd1ccd9f 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 fec31be0068..9a492698afa 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 7230d440e25..b66c9eb34d4 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 6f343879d33..2fc2e89f2a7 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 75327d15d65..f8357b18bd4 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 1e8899eb2aa..916ee57c5ac 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 76a65b7a4cb..ba8f2692045 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 3f464ad1ce6..fb48eef55c6 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 a918bbe89d6..8741ff4366b 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 bd244d9dccc..4b52715ea00 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 aa204cc6f09..7e435308527 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 a7ee4cc9d13..0a25aee3746 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 bf27d1d5c43..dc32fec761a 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 d0405ab9575..67c8f5fc66c 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 7e812406a87..a41823e211d 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 f885a9cbd4f..278cd6b1ca8 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 6f695fab0ef..d38975aa89e 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 b47028d1703..2f944aabc3d 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 98335b431ea..718a1ff7282 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 b8bd7b54d37..76d1000da31 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 c67ddb5f997..b82b1f1ea49 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 c7d8656c1d8..a23e98c7d3f 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 1f0c66b3972..681ff6903c9 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 5c69d8664bb..3e0fb3ccd5c 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 f06712a949f..769dfec7eb1 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 581b1919683..496c587c81c 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 790e550d801..d966c682f03 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 e833068187d..3b2e9922df0 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 a73fb59646d..449d0d52253 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 ffab68c310d..cf6d0439439 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 56989b00497..e16a39022ee 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 b7a8d6d8b4c..3ca85dd9169 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 1ebf5613cab..64bd97a035e 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 8b1027d8c59..62b2adfa961 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 8d3199b9dd0..364c999b08d 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 2bfca548e7c..dbf5b10093b 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 19212aaa59a..f08f968734c 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 06046787322..113cd533318 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 208261b9b8a..58fb3cfd94a 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 ce350752da7..96a4dada98c 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 c684b9c4492..acfd066879a 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 8d071ee30fd..423f5f5461f 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 a137065e563..bb8f87abdde 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 98378dd6485..a5cc1c3b21a 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 f147d979ba6..61abc03375f 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 a9a5faada4c..0b9cfd4ba45 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 4cc52894e3e..1163014207d 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 030bc181055..5fedac77ff9 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 09d5c1d3b60..cdebfeb8107 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 d43f32df88f..5c2e81f278b 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 c2b18f5297b..6f4cad144a5 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 b494b2801b4..e1ff6c039ef 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 0f69ac36e7d..5237c2a4b58 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 5dc0225d981..edd32617837 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 35b5584c4f7..449d0122e33 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 6e5b90db5ca..bf36ca56f08 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 f5aedff7c6b..8d4fccc4569 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 6764c48d108..032d897f8ba 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 c1eee415a1c..31550d4499b 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 995dd6ecd98..f680eedbd8a 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 d90636e13ff..d713208c494 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 5913cff16e2..f27cc9aa4bc 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 b0ad6ea8bf0..7785a02f3e9 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 223ad5c9794..716e7a97fc8 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 8aa473e6a88..9693a65d6f9 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 bc4139b4527..474f7fc2a78 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 4e512ac8805..daeb42e807d 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 f7f1c4f3d5f..fa4011daa70 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 2a9a705fe12..0f2ee44b76b 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 7df319bfe1b..798b10b9795 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 d13f9c26f78..f027e95c45d 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 f2e30c7986c..406231899fb 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 65b83b79667..1f3e4c1b5c5 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 65bec69895d..693f0fa2ad5 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 cd749e6039a..faf2ae20c59 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 932a6a21b59..e08caffa219 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 4949d71365f..d27440ffe80 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 cbd9a175786..080d9db4871 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 20a8de641d0..bb6a05cea88 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 e003c12e219..bdf9fcb605e 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 edbd7578918..3a5bbd5388b 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 2509c12e962..760d26e1be2 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 2290b3c0060..0a295f93b9b 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 dd83495742a..d47ad84189f 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 50559fe579a..6533b47f5f4 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 081e9c20a8c..ae8c550d29c 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 226e955d848..199dc8b0e7c 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 11441e059d8..5b12aaa0679 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 982a2d4cd30..a83e5959c1d 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 8573281083e..408c5bfe29f 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 42d18d4dba9..ae7751adf67 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 91a18f97879..32e3df10595 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 1f71bbf68e6..cc511122847 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 5b8b5c409c8..6ea2df5c10e 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 9022229b0ce..c3fb36c85b0 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 79a864f661b..91a1ea440f6 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 5feb4b7b641..28a71cf12d6 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 2c74a232404..29a5778b6e4 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 af447a6d0ee..0f2ad914679 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 a2798c4fc17..13d543aca0a 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 dcf851b127f..cf38ee596b0 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 11eb832b3dc..62cdb9ffd58 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 daa5846e85f..31b0ba266c7 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 775cb6b9825..6345b07b12f 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 d6ebaa502a0..1bb386bf564 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 5c4c13952e9..b70144b1956 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 5109230a6a9..42e260dfe2e 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 73aef7522e9..5ba713c1065 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 6dbb16c49b5..122568e7918 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 b7198ee4368..6b686d57c2b 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 b91f98f8953..a266f890b79 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 c143c2e1611..4634a3da073 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 06756647d02..2cc356bef6d 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 bceb2a989bb..5960b6f20db 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 0a686335522..108d08b52e6 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 8ae56bcdeb5..058ce45d0d3 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 7745d22c51f..a407df3bbb6 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 4f3036d47a0..fdfd675cae7 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 499768b407a..e08101c8596 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 fc9b74db01f..497cac1d99a 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 c297342db79..ea0238b01f3 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 fc2a046e763..b5aaf72a62b 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 7dcb94522e1..abd3214d7f1 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 a08ea88543f..52b1c714c82 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 01f2848182e..8bd41d9f253 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 571c71bb30a..26b52ff27aa 100644 --- a/apps/sim/app/api/tools/ssh/execute-command/route.ts +++ b/apps/sim/app/api/tools/ssh/execute-command/route.ts @@ -61,7 +61,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 9da4f51027b..673f6e3897b 100644 --- a/apps/sim/app/api/tools/ssh/execute-script/route.ts +++ b/apps/sim/app/api/tools/ssh/execute-script/route.ts @@ -73,7 +73,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 77b148a70d5..a350059fd35 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 @@ -99,7 +99,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 c3bb0e32d1a..4407fda5710 100644 --- a/apps/sim/app/api/tools/ssh/list-directory/route.ts +++ b/apps/sim/app/api/tools/ssh/list-directory/route.ts @@ -103,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 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 cf9114ea205..f61c8fb3551 100644 --- a/apps/sim/app/api/tools/ssh/move-rename/route.ts +++ b/apps/sim/app/api/tools/ssh/move-rename/route.ts @@ -92,7 +92,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 778bbc1f634..2558f1fcf62 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 c85e6418c04..106aeff7c79 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 4dcab77ea2e..9ce1f92b5e2 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 8b0842d5978..a1fa2ce7706 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 5d049559963..c72f96c4e7c 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 44152a4ad52..a320bcce008 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 3e9a1ee8640..e36f88c501a 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 c9c51c7003e..2d685c501fd 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 209bb01557a..48e6f07899f 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 3170c3da4a0..7a82e57eebf 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 ca7186e9b99..3b6f0a78707 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 92cbece9301..ddd0fc9350c 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 f5f7969ff29..84d930d9c7b 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 4779dcb86fa..70b106e3c6c 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 5b9a1cf9383..a62632caea6 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 044dc7c662c..96879dcc14c 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 897124374a7..8cbba58fe1f 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 fae29cdd054..7ed61b992f8 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 9ed21ecdc64..eb57a04418e 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 adbf5304242..7758ec415bd 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 7b44467fe07..96fe04b884b 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 28d2f776630..9bc24a6b745 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 2d7f943d475..878a4048f15 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 a4fb4b09956..9548af4467b 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 0486d88d0c6..e3681a2567e 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 6e19b24466b..12da2faa429 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 a5214731e55..4dfd60b1a0a 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 457445a87ca..82bdd359d5d 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 43aa1991cf8..3c7208cc841 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 2e22d64bc13..0ac8b9ab43f 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 f08b960fe88..a286a655de6 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 8c9842ca0b6..3c854e4e5b1 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 76d7393870f..7532772fb41 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 707a6a3de43..4b1bed44ef9 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 63cb660dbb2..9c4333c4fec 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 2d4312b54be..4f4098953ed 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 84b0e7c0e09..3c8415ea343 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 b53255d9e11..4740909c2f5 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 a46714cd4c6..18d1864daef 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 1ea8721a825..23d574efc41 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 b2d80e8ddb2..71403444637 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 b2bc3fd8fd0..cb77489a39e 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 7d20bf3e3eb..65a8fa96196 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 934f366f110..b2fd323324b 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 55b2059f5ef..dab424729cb 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 6242eb64a01..5706dd1699d 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 89edbb98f22..fb2d52d7d34 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 d14969e131d..d56e7ab6442 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 fe225810380..0d41810c77e 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 cba4dd8a72f..1826988ea08 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 1227f93c504..abb83b3f6c9 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 0b6d4876ab3..fd29a6c9dfb 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 d8d4c2e691c..75caa8542e7 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 697dbdb26e2..aa3a3ccc357 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 cd185473380..f6264ddf0fb 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 8ad86d3222c..5c770a7aac8 100644 --- a/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx +++ b/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx @@ -1,3 +1,4 @@ +import { deepClone } from '@sim/utils/object' import type { Metadata } from 'next' import { PauseResumeManager } from '@/lib/workflows/executor/human-in-the-loop-manager' import ResumeExecutionPage from '@/app/resume/[workflowId]/[executionId]/resume-page-client' @@ -39,7 +40,7 @@ export default async function ResumeExecutionPageWrapper({ return ( ) diff --git a/apps/sim/app/unsubscribe/unsubscribe.tsx b/apps/sim/app/unsubscribe/unsubscribe.tsx index 69dd17eb3a9..3804267ea43 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 2c922ca1d8f..3ec0168918f 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 b35018dba2b..1652e6eb2d2 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 74df75e4b2a..e426cc48e89 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 e25bc031066..6ce52129676 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 f79faf6d8cf..13e4e849c9d 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 87ebc397b72..c9c2931bce2 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 92a184b60ef..5faf30a5872 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 a59f6fd7ef4..fc0f34931e2 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 be1d4ea8129..e8e5a9dea56 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 2992257a055..4773c76a7ad 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 5b4cf3eb3ff..f1be6c1b11b 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 89bf444814e..4f1cbeb6bc9 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 cfdaf31c19e..306b62850cb 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 c52c9d39e3e..01d6d027d93 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 01363668644..d796cb7f307 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 6e32675aa47..ab794a1cda4 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 21b0b001d3c..524e8b16005 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 8ac6a35fd9d..af9bae03929 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,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { deepClone } from '@sim/utils/object' import { Button, Input as EmcnInput, @@ -327,7 +329,7 @@ export function McpServerFormModal({ if (open && !prevOpen) { const data = initialData ?? DEFAULT_FORM_DATA setFormData(data) - setOriginalData(JSON.parse(JSON.stringify(data))) + setOriginalData(deepClone(data)) setFormMode('form') setJsonInput('') setJsonError(null) @@ -520,7 +522,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 +573,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 a500a4d28d2..a21da0f563e 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 95529a6b53f..72dbbf1afca 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,9 @@ 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 { deepClone } from '@sim/utils/object' import { useQueryClient } from '@tanstack/react-query' import { Check, Clipboard, Key, Search } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' @@ -217,7 +220,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 +245,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 +301,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 +317,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 +599,8 @@ export function SecretsManager() { })), createEmptyEnvVar(), ] - initialVarsRef.current = JSON.parse(JSON.stringify(initialVars)) - setEnvVars(JSON.parse(JSON.stringify(initialVars))) + initialVarsRef.current = deepClone(initialVars) + setEnvVars(deepClone(initialVars)) }, [personalEnvData]) useEffect(() => { @@ -782,7 +785,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 +957,7 @@ export function SecretsManager() { } const resetToSaved = () => { - setEnvVars(JSON.parse(JSON.stringify(initialVarsRef.current))) + setEnvVars(deepClone(initialVarsRef.current)) setWorkspaceVars({ ...initialWorkspaceVarsRef.current }) setNewWorkspaceRows([createEmptyEnvVar()]) setShowUnsavedChanges(false) @@ -1035,7 +1038,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 = deepClone(envVars.filter((v) => v.key && v.value)) setWorkspaceVars(mergedWorkspaceVars) setNewWorkspaceRows([createEmptyEnvVar()]) @@ -1088,7 +1091,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 +1119,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 c27379e67fd..0efc4f2c463 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 ebf9a344d21..788465b2057 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 e55e686f66d..7408dc0e849 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 132226fe50d..463793b2fd8 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 e0143fffab7..f00b2b7a211 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 b596beaa60d..a3c217eb634 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 edc50bb4c02..b6f7e5becaa 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 8cc4eda2f71..5ae66012f30 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 f075b9eeee2..4046953ec51 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 ef4d9412262..eb06c387061 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 4451f017625..51a1b1b40dc 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 0534816dc25..681da1f49ed 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 0cc64dfbfa8..efd15dbb0ec 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 61a662d1d6d..0eaed948341 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 ( ( // Ensure we're passing the actual value, not a reference that might change const valueCopy = - newValue === null - ? null - : typeof newValue === 'object' - ? JSON.parse(JSON.stringify(newValue)) - : newValue + newValue === null ? null : typeof newValue === 'object' ? deepClone(newValue) : newValue // If streaming, hold value locally and do not update global store to avoid render-phase updates if (isStreaming) { 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 357d3a030ab..979af8479ae 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 6dfc29760e6..48f1f6ba9fc 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 604d0edd9c4..47211da7b1d 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 7f3aca63afd..564eb796400 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 23d41f3ac27..0d589d24996 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 fdaae3257d7..1baaa3b20b4 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 3ee33e90356..3a0aa50344a 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 cf1398f295e..1882bc120f9 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 9e47e817e77..4ace662d834 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 df502c237ce..5931f36cc9d 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 53bf028ed6c..a447dc54728 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 71ac6822e85..a4e9341ed86 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 cc65c3f038a..702e44f4050 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 2ef6b973ad7..764a4d18e71 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 1ef21536155..48f808cc687 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 ed20942aebc..0418fc54872 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 bf589ff9c0f..51679871692 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 7584783a7b7..c612eb5aca4 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 3334a347cad..0251fb4a697 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 24040aa6413..50d7fa34253 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 a8590e38469..bff9a86e915 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 d107cb0569a..83b1e1fd7b9 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 f132edbc3cb..4371d110ffa 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 d69f91e0da8..38ac6b175a2 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 2395784e80d..04e4db3a678 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 9d703f9505d..7b7362dc3d7 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 f25aafe8154..6aa181fe653 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 97729aca3ce..bfb857085cc 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 40efc9eadaa..49da592e51e 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 b8ab17c9362..9ee72da8552 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 b0081e5af4b..81d750b7f2c 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 b63015df860..ccb5c53dbe9 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 8ac32dc5752..ac310ef90d9 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 bfa3936aedc..638e3e7e42d 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 272ff16a91c..2abe45c9264 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 94eef2f4710..c7f0bf975be 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 f0539ebf4f6..b77b2f4f8f0 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 e2f329c206a..656a241eabb 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -259,7 +259,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 ceeea98a333..02927bda5e4 100644 --- a/apps/sim/executor/utils/json.ts +++ b/apps/sim/executor/utils/json.ts @@ -19,7 +19,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 c32accb2d4d..93d06c0814a 100644 --- a/apps/sim/hooks/queries/mcp.ts +++ b/apps/sim/hooks/queries/mcp.ts @@ -436,7 +436,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 +447,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 63786c71e5d..a8f8570e8b8 100644 --- a/apps/sim/hooks/queries/providers.ts +++ b/apps/sim/hooks/queries/providers.ts @@ -35,7 +35,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 c07fd2a3bff..a9636527734 100644 --- a/apps/sim/hooks/use-execution-stream.ts +++ b/apps/sim/hooks/use-execution-stream.ts @@ -158,7 +158,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 fb6cf002ec5..4164f5e225f 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 5b4c3f6a4f5..de85eadd6a4 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 91b834e8fae..70e6a339427 100644 --- a/apps/sim/lib/billing/client/upgrade.ts +++ b/apps/sim/lib/billing/client/upgrade.ts @@ -103,7 +103,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 +202,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 496f3b0cbb1..53913192346 100644 --- a/apps/sim/lib/billing/credits/purchase.ts +++ b/apps/sim/lib/billing/credits/purchase.ts @@ -226,7 +226,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 521ee6d0661..9eaa77b43d3 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 c9d87b3ecc5..8ebd865df03 100644 --- a/apps/sim/lib/copilot/request/go/stream.ts +++ b/apps/sim/lib/copilot/request/go/stream.ts @@ -334,7 +334,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 +451,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 7fe971055a0..40af3110c62 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 802bcec329b..1056ad0bee5 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 381e91a3953..038aa6e483d 100644 --- a/apps/sim/lib/copilot/request/lifecycle/headless.ts +++ b/apps/sim/lib/copilot/request/lifecycle/headless.ts @@ -97,7 +97,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 23628a0584b..e6f9a550d02 100644 --- a/apps/sim/lib/copilot/request/lifecycle/start.ts +++ b/apps/sim/lib/copilot/request/lifecycle/start.ts @@ -203,7 +203,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 +291,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 +327,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 +356,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 +399,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 2d116e87ce4..d084690186e 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 8bad72847a0..ab7e2e236de 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 e683403736a..9387e71f93e 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 2543aed56af..a6b571c0441 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 74a91b73c53..3320288f5f1 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 d58989cb563..7aa2b88c724 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 a96cd5d3bfc..ab87b264aa5 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 0573a9bc363..c9b8a5e4b61 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 22d9539ba88..b668338120c 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 c6e904ec7b3..ae303b4d0a7 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 78eb3c08cb0..d4d0d115295 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 b5428075a07..540dd83d217 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 3ee9d41dbf7..7e6ed763526 100644 --- a/apps/sim/lib/core/security/csp.test.ts +++ b/apps/sim/lib/core/security/csp.test.ts @@ -1,4 +1,5 @@ import { createEnvMock, featureFlagsMock } from '@sim/testing' +import { deepClone } from '@sim/utils/object' import { afterEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/core/config/env', () => @@ -185,7 +186,7 @@ describe('generateRuntimeCSP', () => { }) describe('addCSPSource', () => { - const originalDirectives = JSON.parse(JSON.stringify(buildTimeCSPDirectives)) + const originalDirectives = deepClone(buildTimeCSPDirectives) afterEach(() => { Object.keys(buildTimeCSPDirectives).forEach((key) => { @@ -222,7 +223,7 @@ describe('addCSPSource', () => { }) describe('removeCSPSource', () => { - const originalDirectives = JSON.parse(JSON.stringify(buildTimeCSPDirectives)) + const originalDirectives = deepClone(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 3d2e7390ec1..2ffaff4ef23 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 b5a3c0896ab..76ff4dfdb91 100644 --- a/apps/sim/lib/core/security/redaction.test.ts +++ b/apps/sim/lib/core/security/redaction.test.ts @@ -1,3 +1,4 @@ +import { deepClone } from '@sim/utils/object' import { describe, expect, it } from 'vitest' import { isLargeDataKey, @@ -537,7 +538,7 @@ describe('Security edge cases', () => { }, } - const originalCopy = JSON.parse(JSON.stringify(original)) + const originalCopy = deepClone(original) redactApiKeys(original) expect(original).toEqual(originalCopy) diff --git a/apps/sim/lib/core/telemetry.ts b/apps/sim/lib/core/telemetry.ts index 34af72809f1..6751b4fa1e3 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 5670d6a5d52..0cf94f82fa1 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 8a073ec4648..5b3212f23e5 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 b7959d75f50..c01779c163a 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 d9b50d36688..6a56c00ad95 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 492e3bf02b2..40e288e7117 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 e931bf9a526..23a8a5665dc 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 d9c59f8887c..eb220a04398 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 3ea10f828ca..4e9cd289550 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 6f26baae83c..cc3d1461e41 100644 --- a/apps/sim/lib/environment/utils.ts +++ b/apps/sim/lib/environment/utils.ts @@ -167,7 +167,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 8c0d08090cb..5afe0c4ccaf 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 d5b4e775865..f19cebf61c7 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 05b23940a9f..658cb19afcc 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 cf1eb00367b..8f61961e48c 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 ac239fb6e71..2c84cec52af 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 c055e848d69..7af7743a296 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 22c79598c59..7da40aebd18 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 a5b5223fba2..67f246d06b5 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 49675608309..204b5179113 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 597c64602fa..0a2221cdc33 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 0918750b153..93588aecdd3 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 943ad4d8038..4ec764c3d41 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 0db17924355..c97944288fd 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 ae7d4579cfb..8ef9dc25ac6 100644 --- a/apps/sim/lib/messaging/email/mailer.ts +++ b/apps/sim/lib/messaging/email/mailer.ts @@ -345,7 +345,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 +157,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 +283,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 +352,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 +460,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 aa241d05f1e..c521f5fe4cc 100644 --- a/apps/sim/lib/mothership/inbox/response.ts +++ b/apps/sim/lib/mothership/inbox/response.ts @@ -54,7 +54,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 23ea7498cb8..6c401a78765 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 67f01eb4190..a56ef2ca9e4 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 4665b6fc228..1fb9c435099 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 94c35b2ade5..4cda4b64419 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 3738eea03e7..89b3bd37d91 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 69077c663de..5d62fb4be7e 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 06abed145f6..cc85dc8c930 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 b417ec4cd1e..f7740127b13 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 7bc2a8051e1..ecd343d1e4d 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 81376c54d29..6905d36c06f 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 15443a63da8..596f81f3a59 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 19a52f16d70..660c81f4972 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 4009c70ae8a..15de4122d0e 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 449844042c8..ee35e7df12a 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 84698d05cf2..a920553f16b 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 b3621899418..7b50c43c1b1 100644 --- a/apps/sim/lib/workflows/autolayout/index.ts +++ b/apps/sim/lib/workflows/autolayout/index.ts @@ -1,4 +1,6 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { deepClone } from '@sim/utils/object' import { DEFAULT_HORIZONTAL_SPACING, DEFAULT_VERTICAL_SPACING, @@ -31,7 +33,7 @@ export function applyAutoLayout( edgeCount: edges.length, }) - const blocksCopy: Record = JSON.parse(JSON.stringify(blocks)) + const blocksCopy: Record = deepClone(blocks) const horizontalSpacing = options.horizontalSpacing ?? DEFAULT_HORIZONTAL_SPACING const verticalSpacing = options.verticalSpacing ?? DEFAULT_VERTICAL_SPACING @@ -86,7 +88,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 33c0fb3f345..45ec50a4b01 100644 --- a/apps/sim/lib/workflows/autolayout/targeted.ts +++ b/apps/sim/lib/workflows/autolayout/targeted.ts @@ -1,3 +1,4 @@ +import { deepClone } from '@sim/utils/object' import { CONTAINER_PADDING, DEFAULT_HORIZONTAL_SPACING, @@ -62,7 +63,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 = deepClone(blocks) prepareContainerDimensions( blocksCopy, diff --git a/apps/sim/lib/workflows/colors.ts b/apps/sim/lib/workflows/colors.ts index d7acbb74abf..0b16c72b8b4 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 e66d76b8bce..651a03ad503 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 3ec6c73bb6c..e91cf1f0a78 100644 --- a/apps/sim/lib/workflows/credentials/credential-extractor.ts +++ b/apps/sim/lib/workflows/credentials/credential-extractor.ts @@ -1,3 +1,4 @@ +import { deepClone } from '@sim/utils/object' import { buildCanonicalIndex, buildSubBlockValues, @@ -238,7 +239,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 = deepClone(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 d805945254e..670ac63ca78 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 c099ce3151b..589c4e32363 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 8d54ec72dff..7a4b7577ac1 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 45a188130ce..ef0c4379cf1 100644 --- a/apps/sim/lib/workflows/persistence/duplicate.ts +++ b/apps/sim/lib/workflows/persistence/duplicate.ts @@ -8,6 +8,7 @@ import { } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' +import { deepClone } from '@sim/utils/object' import { authorizeWorkflowByWorkspacePermission, FolderLockedError } from '@sim/workflow-authz' import { and, eq, isNull, min } from 'drizzle-orm' import type { DbOrTx } from '@/lib/db/types' @@ -580,9 +581,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 = deepClone(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 e6b9dbb086a..58bd43b5644 100644 --- a/apps/sim/lib/workflows/persistence/utils.test.ts +++ b/apps/sim/lib/workflows/persistence/utils.test.ts @@ -17,6 +17,7 @@ import { createStarterBlock, createWorkflowState, } from '@sim/testing' +import { deepClone } from '@sim/utils/object' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { BlockState as AppBlockState, @@ -767,7 +768,7 @@ describe('Database Helpers', () => { mockDb.transaction = mockTransaction - const staleWorkflowState = JSON.parse(JSON.stringify(mockWorkflowState)) + const staleWorkflowState = deepClone(mockWorkflowState) staleWorkflowState.loops = {} staleWorkflowState.parallels = {} diff --git a/apps/sim/lib/workflows/persistence/utils.ts b/apps/sim/lib/workflows/persistence/utils.ts index bea2f042e13..330da756141 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: deepClone(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 5222499fc3f..00d6e7075a3 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 6a82d4d4c63..3639cb7cd5d 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 2c8356c0bc4..6ba56f5d5b8 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 3336f17a9c2..f33dd239471 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 b652ebd36d9..148ac7c0229 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 3c050926402..5bb9f80a789 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 ab080bcceec..864c9fc6b65 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 6ab45379f8d..b9b001bb7a2 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 5f78c227ade..3ddc32f0874 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 22500b84c07..d7f8d53ce24 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 9400d2378f1..401c264c0c3 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 795cdaa498e..70929f23bfc 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 fe06798d728..d49a3383611 100644 --- a/apps/sim/providers/deepseek/index.ts +++ b/apps/sim/providers/deepseek/index.ts @@ -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 5b01076bf07..9f20af63484 100644 --- a/apps/sim/providers/fireworks/index.ts +++ b/apps/sim/providers/fireworks/index.ts @@ -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 6536d6336f6..78c4780a44f 100644 --- a/apps/sim/providers/gemini/core.ts +++ b/apps/sim/providers/gemini/core.ts @@ -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 4165724bec8..e1decf9c4e7 100644 --- a/apps/sim/providers/groq/index.ts +++ b/apps/sim/providers/groq/index.ts @@ -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 ce8959f3048..47c24a2a8c4 100644 --- a/apps/sim/providers/mistral/index.ts +++ b/apps/sim/providers/mistral/index.ts @@ -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 111956140c8..545dda87e11 100644 --- a/apps/sim/providers/ollama/index.ts +++ b/apps/sim/providers/ollama/index.ts @@ -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 1b2591d22e2..f623a396f90 100644 --- a/apps/sim/providers/openai/core.ts +++ b/apps/sim/providers/openai/core.ts @@ -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 09a8adbbdd5..7cc2412b6ff 100644 --- a/apps/sim/providers/openrouter/index.ts +++ b/apps/sim/providers/openrouter/index.ts @@ -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 088686500b3..8b1256c2de7 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 780776aed36..a646214b148 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 b182eefcd91..d1e9d318231 100644 --- a/apps/sim/providers/vllm/index.ts +++ b/apps/sim/providers/vllm/index.ts @@ -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 bd6303007df..ee73860997e 100644 --- a/apps/sim/providers/xai/index.ts +++ b/apps/sim/providers/xai/index.ts @@ -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 d3c71e2600e..3b1e0e91b39 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 b9c099c4889..428cecf6f4d 100644 --- a/apps/sim/stores/workflows/registry/store.ts +++ b/apps/sim/stores/workflows/registry/store.ts @@ -1,4 +1,6 @@ import { createLogger } from '@sim/logger' +import { deepClone } from '@sim/utils/object' +import { generateRandomHex } from '@sim/utils/random' import { create } from 'zustand' import { devtools } from 'zustand/middleware' import { requestJson } from '@/lib/api/client/request' @@ -25,7 +27,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 +328,11 @@ export const useWorkflowRegistry = create()( blockIdSet.forEach((blockId) => { const block = workflowStore.blocks[blockId] if (block) { - copiedBlocks[blockId] = JSON.parse(JSON.stringify(block)) + copiedBlocks[blockId] = deepClone(block) if (activeWorkflowId) { const blockValues = subBlockStore.workflowValues[activeWorkflowId]?.[blockId] if (blockValues) { - copiedSubBlockValues[blockId] = JSON.parse(JSON.stringify(blockValues)) + copiedSubBlockValues[blockId] = deepClone(blockValues) } } } @@ -343,14 +345,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] = deepClone(loop) } }) const copiedParallels: Record = {} Object.entries(workflowStore.parallels).forEach(([parallelId, parallel]) => { if (blockIdSet.has(parallelId)) { - copiedParallels[parallelId] = JSON.parse(JSON.stringify(parallel)) + copiedParallels[parallelId] = deepClone(parallel) } }) diff --git a/apps/sim/stores/workflows/registry/utils.ts b/apps/sim/stores/workflows/registry/utils.ts index 8be102e4a60..e893b17afc4 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 fbb97f22fca..34626835798 100644 --- a/apps/sim/stores/workflows/utils.ts +++ b/apps/sim/stores/workflows/utils.ts @@ -1,4 +1,5 @@ import { generateId } from '@sim/utils/id' +import { deepClone } from '@sim/utils/object' import { mergeSubblockStateWithValues } from '@sim/workflow-persistence/subblocks' import type { Edge } from 'reactflow' import { DEFAULT_DUPLICATE_OFFSET } from '@/lib/workflows/autolayout/constants' @@ -351,7 +352,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: deepClone(block.subBlocks) } remapConditionIds(newBlock.subBlocks, {}, oldId, newId) newBlocks[newId] = newBlock }) @@ -521,7 +522,7 @@ export function regenerateBlockIds( id: newId, name: newName, position: newPosition, - subBlocks: JSON.parse(JSON.stringify(block.subBlocks)), + subBlocks: deepClone(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 +534,7 @@ export function regenerateBlockIds( allBlocksForNaming[newId] = newBlock if (subBlockValues[oldId]) { - newSubBlockValues[newId] = JSON.parse(JSON.stringify(subBlockValues[oldId])) + newSubBlockValues[newId] = deepClone(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 e6fd406b80e..b7227321708 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -1,6 +1,7 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' +import { deepClone } from '@sim/utils/object' import type { Edge } from 'reactflow' import { create } from 'zustand' import { devtools } from 'zustand/middleware' @@ -648,7 +649,7 @@ export const useWorkflowStore = create()( ...acc, [subId]: { ...subBlock, - value: JSON.parse(JSON.stringify(subBlock.value)), + value: deepClone(subBlock.value), }, }), {} @@ -656,11 +657,7 @@ export const useWorkflowStore = create()( // Remap condition/router IDs in the duplicated subBlocks const clonedSubBlockValues = activeWorkflowId - ? JSON.parse( - JSON.stringify( - useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[id] || {} - ) - ) + ? deepClone(useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[id] || {}) : {} remapConditionIds( newSubBlocks as Record, diff --git a/apps/sim/tools/databricks/run_job.ts b/apps/sim/tools/databricks/run_job.ts index 6117ebcb18b..d4739488603 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 5de6f0ae2e4..a4653d3a438 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 3722dbe0dfd..ff5701ed78a 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 d5bef81c22a..2fd7281dadc 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 6c063c38642..e2459bcde83 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 f6f144af8a9..143749c6409 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 f19ba67626c..4ec92262363 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 { 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 6dbeb56992b..123ca34bd42 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 ed3bd8d198f..d46d17b5a79 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 c608c45737c..cd7dac4b9eb 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 2fa35b5391d..fba9e542e1d 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 0d6640d9da7..8fdfe7fbf90 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 a079746d84d..41df8750aa2 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 25b3fb3df32..1dc83f876e4 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 293d6f545cc..5efd45742b5 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 dcab3170e20..10b144ccae9 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 138630fc623..fed0dc79664 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 ce074d8f064..39075e46957 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 1020f651f8e..57b947afb16 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 2b3bdd74cf9..d9eb938c54f 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 65e6e69f082..1bdcf377493 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 38df3cb716f..4a80d37431f 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 7440c9c6d61..a41e6575f88 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 c98babab91a..662c0ac4778 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 249b5ac8fe5..4a142669be2 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 b0684c02125..f6953969712 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 ffed7ca1e7b..bde2321b68f 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 013c600c567..d02e9bd46a8 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 5bfc309c1c5..48fcee083c3 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 83147e181a0..fdd6d775aa1 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 { deepClone, 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 00000000000..e8992ca40b9 --- /dev/null +++ b/packages/utils/src/object.ts @@ -0,0 +1,36 @@ +/** + * Creates a deep clone of a value using the platform's structured-clone + * algorithm. Handles Dates, Maps, Sets, ArrayBuffers, and other structured + * types correctly. Does not clone functions or non-serializable objects. + * + * Replaces the common `JSON.parse(JSON.stringify(obj))` pattern, which loses + * type information (Dates become strings, undefined values are dropped). + */ +export function deepClone(value: T): T { + return structuredClone(value) +} + +/** + * 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 00000000000..da00e83f41f --- /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 00000000000..7b842c996e5 --- /dev/null +++ b/packages/utils/src/retry.ts @@ -0,0 +1,59 @@ +import { randomFloat } from './random.js' + +/** 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) + return exponential * (0.8 + randomFloat() * 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 00000000000..217b1584d48 --- /dev/null +++ b/packages/utils/src/string.ts @@ -0,0 +1,12 @@ +/** + * Truncates `str` to at most `maxLength` characters, appending `suffix` when + * truncation occurs. Defaults to `'...'`. + * + * @example + * truncate('hello world', 8) // 'hello...' + * truncate('hello world', 8, ' …') // 'hello …' + * truncate('hi', 10) // 'hi' + */ +export function truncate(str: string, maxLength: number, suffix = '...'): string { + return str.length > maxLength ? str.slice(0, maxLength) + suffix : str +} diff --git a/scripts/check-utils-enforcement.ts b/scripts/check-utils-enforcement.ts new file mode 100644 index 00000000000..2967b615a6e --- /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: 'deepClone() from @sim/utils/object', + }, + // 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() From b488fffcd3232ad20331c4ccf95982f57bb71bf8 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 15 May 2026 16:54:08 -0700 Subject: [PATCH 2/6] 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 --- .claude/rules/global.md | 6 +++--- CLAUDE.md | 2 +- .../app/resume/[workflowId]/[executionId]/page.tsx | 3 +-- .../mcp-server-form-modal/mcp-server-form-modal.tsx | 3 +-- .../settings/components/secrets/secrets-manager.tsx | 9 ++++----- .../sub-block/hooks/use-sub-block-value.ts | 7 +++++-- .../tools/server/workflow/edit-workflow/engine.ts | 3 +-- apps/sim/lib/core/security/csp.test.ts | 5 ++--- apps/sim/lib/core/security/redaction.test.ts | 3 +-- apps/sim/lib/workflows/autolayout/index.ts | 3 +-- apps/sim/lib/workflows/autolayout/targeted.ts | 3 +-- .../workflows/credentials/credential-extractor.ts | 3 +-- apps/sim/lib/workflows/persistence/duplicate.ts | 3 +-- apps/sim/lib/workflows/persistence/utils.test.ts | 3 +-- apps/sim/lib/workflows/persistence/utils.ts | 2 +- apps/sim/stores/workflows/registry/store.ts | 9 ++++----- apps/sim/stores/workflows/utils.ts | 7 +++---- apps/sim/stores/workflows/workflow/store.ts | 7 ++++--- apps/sim/tools/openai/image.ts | 3 +-- packages/utils/src/index.ts | 2 +- packages/utils/src/object.ts | 12 ------------ scripts/check-utils-enforcement.ts | 2 +- 22 files changed, 39 insertions(+), 61 deletions(-) diff --git a/.claude/rules/global.md b/.claude/rules/global.md index 4da51a0c70d..4d47c82f60a 100644 --- a/.claude/rules/global.md +++ b/.claude/rules/global.md @@ -39,7 +39,7 @@ Use shared helpers from `@sim/utils` instead of writing inline implementations: - `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'` -- `deepClone(value)` from `@sim/utils/object` — structural clone. Never write `JSON.parse(JSON.stringify(obj))` +- `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 @@ -56,10 +56,10 @@ const filtered = Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== // ✓ Good import { sleep } from '@sim/utils/helpers' import { getErrorMessage, toError } from '@sim/utils/errors' -import { deepClone, filterUndefined } from '@sim/utils/object' +import { filterUndefined } from '@sim/utils/object' await sleep(1000) const msg = getErrorMessage(error, 'Unknown error') -const clone = deepClone(obj) +const clone = structuredClone(obj) const filtered = filterUndefined(obj) ``` diff --git a/CLAUDE.md b/CLAUDE.md index 9a8e18cc95a..1b6a602ef22 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,7 @@ You are a professional software engineer. All code must follow best practices: a - `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'` - - `deepClone(value)` from `@sim/utils/object` — structural clone; never `JSON.parse(JSON.stringify(...))` + - `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 diff --git a/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx b/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx index 5c770a7aac8..67114faec43 100644 --- a/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx +++ b/apps/sim/app/resume/[workflowId]/[executionId]/page.tsx @@ -1,4 +1,3 @@ -import { deepClone } from '@sim/utils/object' import type { Metadata } from 'next' import { PauseResumeManager } from '@/lib/workflows/executor/human-in-the-loop-manager' import ResumeExecutionPage from '@/app/resume/[workflowId]/[executionId]/resume-page-client' @@ -40,7 +39,7 @@ export default async function ResumeExecutionPageWrapper({ return ( ) 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 af9bae03929..79209191d46 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 @@ -3,7 +3,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { deepClone } from '@sim/utils/object' import { Button, Input as EmcnInput, @@ -329,7 +328,7 @@ export function McpServerFormModal({ if (open && !prevOpen) { const data = initialData ?? DEFAULT_FORM_DATA setFormData(data) - setOriginalData(deepClone(data)) + setOriginalData(structuredClone(data)) setFormMode('form') setJsonInput('') setJsonError(null) 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 72dbbf1afca..39798d3e3ce 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 @@ -4,7 +4,6 @@ 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 { deepClone } from '@sim/utils/object' import { useQueryClient } from '@tanstack/react-query' import { Check, Clipboard, Key, Search } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' @@ -599,8 +598,8 @@ export function SecretsManager() { })), createEmptyEnvVar(), ] - initialVarsRef.current = deepClone(initialVars) - setEnvVars(deepClone(initialVars)) + initialVarsRef.current = structuredClone(initialVars) + setEnvVars(structuredClone(initialVars)) }, [personalEnvData]) useEffect(() => { @@ -957,7 +956,7 @@ export function SecretsManager() { } const resetToSaved = () => { - setEnvVars(deepClone(initialVarsRef.current)) + setEnvVars(structuredClone(initialVarsRef.current)) setWorkspaceVars({ ...initialWorkspaceVarsRef.current }) setNewWorkspaceRows([createEmptyEnvVar()]) setShowUnsavedChanges(false) @@ -1038,7 +1037,7 @@ export function SecretsManager() { if (firstFailure) throw firstFailure.reason initialWorkspaceVarsRef.current = { ...mergedWorkspaceVars } - initialVarsRef.current = deepClone(envVars.filter((v) => v.key && v.value)) + initialVarsRef.current = structuredClone(envVars.filter((v) => v.key && v.value)) setWorkspaceVars(mergedWorkspaceVars) setNewWorkspaceRows([createEmptyEnvVar()]) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts index 8149c2b806a..ca41082f777 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts @@ -1,6 +1,5 @@ import { useCallback, useEffect, useRef } from 'react' import { createLogger } from '@sim/logger' -import { deepClone } from '@sim/utils/object' import { isEqual } from 'es-toolkit' import { useShallow } from 'zustand/react/shallow' import { useStoreWithEqualityFn } from 'zustand/traditional' @@ -150,7 +149,11 @@ export function useSubBlockValue( // Ensure we're passing the actual value, not a reference that might change const valueCopy = - newValue === null ? null : typeof newValue === 'object' ? deepClone(newValue) : newValue + newValue === null + ? null + : typeof newValue === 'object' + ? structuredClone(newValue) + : newValue // If streaming, hold value locally and do not update global store to avoid render-phase updates if (isStreaming) { diff --git a/apps/sim/lib/copilot/tools/server/workflow/edit-workflow/engine.ts b/apps/sim/lib/copilot/tools/server/workflow/edit-workflow/engine.ts index ae9d3b1492d..8d4cfe002da 100644 --- a/apps/sim/lib/copilot/tools/server/workflow/edit-workflow/engine.ts +++ b/apps/sim/lib/copilot/tools/server/workflow/edit-workflow/engine.ts @@ -1,5 +1,4 @@ import { createLogger } from '@sim/logger' -import { deepClone } from '@sim/utils/object' import type { PermissionGroupConfig } from '@/lib/permission-groups/types' import { isValidKey } from '@/lib/workflows/sanitization/key-validation' import { validateEdges } from '@/stores/workflows/workflow/edge-validation' @@ -153,7 +152,7 @@ export function applyOperationsToWorkflowState( permissionConfig: PermissionGroupConfig | null = null ): ApplyOperationsResult { // Deep clone the workflow state to avoid mutations - const modifiedState = deepClone(workflowState) + const modifiedState = structuredClone(workflowState) // Collect validation errors across all operations const validationErrors: ValidationError[] = [] diff --git a/apps/sim/lib/core/security/csp.test.ts b/apps/sim/lib/core/security/csp.test.ts index 7e6ed763526..9f315502262 100644 --- a/apps/sim/lib/core/security/csp.test.ts +++ b/apps/sim/lib/core/security/csp.test.ts @@ -1,5 +1,4 @@ import { createEnvMock, featureFlagsMock } from '@sim/testing' -import { deepClone } from '@sim/utils/object' import { afterEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/core/config/env', () => @@ -186,7 +185,7 @@ describe('generateRuntimeCSP', () => { }) describe('addCSPSource', () => { - const originalDirectives = deepClone(buildTimeCSPDirectives) + const originalDirectives = structuredClone(buildTimeCSPDirectives) afterEach(() => { Object.keys(buildTimeCSPDirectives).forEach((key) => { @@ -223,7 +222,7 @@ describe('addCSPSource', () => { }) describe('removeCSPSource', () => { - const originalDirectives = deepClone(buildTimeCSPDirectives) + const originalDirectives = structuredClone(buildTimeCSPDirectives) afterEach(() => { Object.keys(buildTimeCSPDirectives).forEach((key) => { diff --git a/apps/sim/lib/core/security/redaction.test.ts b/apps/sim/lib/core/security/redaction.test.ts index 76ff4dfdb91..3c9af9b3ed3 100644 --- a/apps/sim/lib/core/security/redaction.test.ts +++ b/apps/sim/lib/core/security/redaction.test.ts @@ -1,4 +1,3 @@ -import { deepClone } from '@sim/utils/object' import { describe, expect, it } from 'vitest' import { isLargeDataKey, @@ -538,7 +537,7 @@ describe('Security edge cases', () => { }, } - const originalCopy = deepClone(original) + const originalCopy = structuredClone(original) redactApiKeys(original) expect(original).toEqual(originalCopy) diff --git a/apps/sim/lib/workflows/autolayout/index.ts b/apps/sim/lib/workflows/autolayout/index.ts index 7b50c43c1b1..2bbfca7f992 100644 --- a/apps/sim/lib/workflows/autolayout/index.ts +++ b/apps/sim/lib/workflows/autolayout/index.ts @@ -1,6 +1,5 @@ import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { deepClone } from '@sim/utils/object' import { DEFAULT_HORIZONTAL_SPACING, DEFAULT_VERTICAL_SPACING, @@ -33,7 +32,7 @@ export function applyAutoLayout( edgeCount: edges.length, }) - const blocksCopy: Record = deepClone(blocks) + const blocksCopy: Record = structuredClone(blocks) const horizontalSpacing = options.horizontalSpacing ?? DEFAULT_HORIZONTAL_SPACING const verticalSpacing = options.verticalSpacing ?? DEFAULT_VERTICAL_SPACING diff --git a/apps/sim/lib/workflows/autolayout/targeted.ts b/apps/sim/lib/workflows/autolayout/targeted.ts index 45ec50a4b01..e6021885fef 100644 --- a/apps/sim/lib/workflows/autolayout/targeted.ts +++ b/apps/sim/lib/workflows/autolayout/targeted.ts @@ -1,4 +1,3 @@ -import { deepClone } from '@sim/utils/object' import { CONTAINER_PADDING, DEFAULT_HORIZONTAL_SPACING, @@ -63,7 +62,7 @@ export function applyTargetedLayout( const changedSet = new Set(changedBlockIds) const resizedSet = new Set(resizedBlockIds) const shiftSourceSet = new Set(shiftSourceBlockIds) - const blocksCopy: Record = deepClone(blocks) + const blocksCopy: Record = structuredClone(blocks) prepareContainerDimensions( blocksCopy, diff --git a/apps/sim/lib/workflows/credentials/credential-extractor.ts b/apps/sim/lib/workflows/credentials/credential-extractor.ts index e91cf1f0a78..9540c5891d7 100644 --- a/apps/sim/lib/workflows/credentials/credential-extractor.ts +++ b/apps/sim/lib/workflows/credentials/credential-extractor.ts @@ -1,4 +1,3 @@ -import { deepClone } from '@sim/utils/object' import { buildCanonicalIndex, buildSubBlockValues, @@ -239,7 +238,7 @@ export function sanitizeWorkflowForSharing( preserveEnvVars?: boolean // Keep {{VAR}} references for export } = {} ): SanitizedWorkflowState { - const sanitized = deepClone(state) as SanitizedWorkflowState + const sanitized = structuredClone(state) as SanitizedWorkflowState if (!sanitized?.blocks) { return sanitized diff --git a/apps/sim/lib/workflows/persistence/duplicate.ts b/apps/sim/lib/workflows/persistence/duplicate.ts index ef0c4379cf1..b6f6709db8c 100644 --- a/apps/sim/lib/workflows/persistence/duplicate.ts +++ b/apps/sim/lib/workflows/persistence/duplicate.ts @@ -8,7 +8,6 @@ import { } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' -import { deepClone } from '@sim/utils/object' import { authorizeWorkflowByWorkspacePermission, FolderLockedError } from '@sim/workflow-authz' import { and, eq, isNull, min } from 'drizzle-orm' import type { DbOrTx } from '@/lib/db/types' @@ -581,7 +580,7 @@ export async function duplicateWorkflow( | LoopConfig | ParallelConfig if (subflow.config && typeof subflow.config === 'object') { - updatedConfig = deepClone(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 58bd43b5644..1059c61caf7 100644 --- a/apps/sim/lib/workflows/persistence/utils.test.ts +++ b/apps/sim/lib/workflows/persistence/utils.test.ts @@ -17,7 +17,6 @@ import { createStarterBlock, createWorkflowState, } from '@sim/testing' -import { deepClone } from '@sim/utils/object' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { BlockState as AppBlockState, @@ -768,7 +767,7 @@ describe('Database Helpers', () => { mockDb.transaction = mockTransaction - const staleWorkflowState = deepClone(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 330da756141..d30b03d5312 100644 --- a/apps/sim/lib/workflows/persistence/utils.ts +++ b/apps/sim/lib/workflows/persistence/utils.ts @@ -717,7 +717,7 @@ export function regenerateWorkflowStateIds(state: RegenerateStateInput): Regener const newBlock: BlockState = { ...block, id: newId, - subBlocks: deepClone(block.subBlocks), + subBlocks: structuredClone(block.subBlocks), locked: false, } diff --git a/apps/sim/stores/workflows/registry/store.ts b/apps/sim/stores/workflows/registry/store.ts index 428cecf6f4d..9cf4519ff6f 100644 --- a/apps/sim/stores/workflows/registry/store.ts +++ b/apps/sim/stores/workflows/registry/store.ts @@ -1,5 +1,4 @@ import { createLogger } from '@sim/logger' -import { deepClone } from '@sim/utils/object' import { generateRandomHex } from '@sim/utils/random' import { create } from 'zustand' import { devtools } from 'zustand/middleware' @@ -328,11 +327,11 @@ export const useWorkflowRegistry = create()( blockIdSet.forEach((blockId) => { const block = workflowStore.blocks[blockId] if (block) { - copiedBlocks[blockId] = deepClone(block) + copiedBlocks[blockId] = structuredClone(block) if (activeWorkflowId) { const blockValues = subBlockStore.workflowValues[activeWorkflowId]?.[blockId] if (blockValues) { - copiedSubBlockValues[blockId] = deepClone(blockValues) + copiedSubBlockValues[blockId] = structuredClone(blockValues) } } } @@ -345,14 +344,14 @@ export const useWorkflowRegistry = create()( const copiedLoops: Record = {} Object.entries(workflowStore.loops).forEach(([loopId, loop]) => { if (blockIdSet.has(loopId)) { - copiedLoops[loopId] = deepClone(loop) + copiedLoops[loopId] = structuredClone(loop) } }) const copiedParallels: Record = {} Object.entries(workflowStore.parallels).forEach(([parallelId, parallel]) => { if (blockIdSet.has(parallelId)) { - copiedParallels[parallelId] = deepClone(parallel) + copiedParallels[parallelId] = structuredClone(parallel) } }) diff --git a/apps/sim/stores/workflows/utils.ts b/apps/sim/stores/workflows/utils.ts index 34626835798..ceee7b786ef 100644 --- a/apps/sim/stores/workflows/utils.ts +++ b/apps/sim/stores/workflows/utils.ts @@ -1,5 +1,4 @@ import { generateId } from '@sim/utils/id' -import { deepClone } from '@sim/utils/object' import { mergeSubblockStateWithValues } from '@sim/workflow-persistence/subblocks' import type { Edge } from 'reactflow' import { DEFAULT_DUPLICATE_OFFSET } from '@/lib/workflows/autolayout/constants' @@ -352,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: deepClone(block.subBlocks) } + const newBlock = { ...block, id: newId, subBlocks: structuredClone(block.subBlocks) } remapConditionIds(newBlock.subBlocks, {}, oldId, newId) newBlocks[newId] = newBlock }) @@ -522,7 +521,7 @@ export function regenerateBlockIds( id: newId, name: newName, position: newPosition, - subBlocks: deepClone(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 @@ -534,7 +533,7 @@ export function regenerateBlockIds( allBlocksForNaming[newId] = newBlock if (subBlockValues[oldId]) { - newSubBlockValues[newId] = deepClone(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 b7227321708..0e8f011ede6 100644 --- a/apps/sim/stores/workflows/workflow/store.ts +++ b/apps/sim/stores/workflows/workflow/store.ts @@ -1,7 +1,6 @@ import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' -import { deepClone } from '@sim/utils/object' import type { Edge } from 'reactflow' import { create } from 'zustand' import { devtools } from 'zustand/middleware' @@ -649,7 +648,7 @@ export const useWorkflowStore = create()( ...acc, [subId]: { ...subBlock, - value: deepClone(subBlock.value), + value: structuredClone(subBlock.value), }, }), {} @@ -657,7 +656,9 @@ export const useWorkflowStore = create()( // Remap condition/router IDs in the duplicated subBlocks const clonedSubBlockValues = activeWorkflowId - ? deepClone(useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[id] || {}) + ? structuredClone( + useSubBlockStore.getState().workflowValues[activeWorkflowId]?.[id] || {} + ) : {} remapConditionIds( newSubBlocks as Record, diff --git a/apps/sim/tools/openai/image.ts b/apps/sim/tools/openai/image.ts index f484d759173..2d5ee547873 100644 --- a/apps/sim/tools/openai/image.ts +++ b/apps/sim/tools/openai/image.ts @@ -1,5 +1,4 @@ import { createLogger } from '@sim/logger' -import { deepClone } from '@sim/utils/object' import { getInternalApiBaseUrl } from '@/lib/core/utils/urls' import type { BaseImageRequestBody } from '@/tools/openai/types' import type { ToolConfig } from '@/tools/types' @@ -110,7 +109,7 @@ export const imageTool: ToolConfig = { try { const data = await response.json() - const sanitizedData = deepClone(data) + const sanitizedData = structuredClone(data) if (sanitizedData.data && Array.isArray(sanitizedData.data)) { sanitizedData.data.forEach((item: { b64_json?: string }) => { if (item.b64_json) { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index fdd6d775aa1..d40a413271e 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -12,7 +12,7 @@ export { } from './formatting.js' export { noop, sleep } from './helpers.js' export { generateId, generateShortId, isValidUuid } from './id.js' -export { deepClone, filterUndefined, omit } from './object.js' +export { filterUndefined, omit } from './object.js' export { generateRandomBytes, generateRandomHex, diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts index e8992ca40b9..b16f0f5c956 100644 --- a/packages/utils/src/object.ts +++ b/packages/utils/src/object.ts @@ -1,15 +1,3 @@ -/** - * Creates a deep clone of a value using the platform's structured-clone - * algorithm. Handles Dates, Maps, Sets, ArrayBuffers, and other structured - * types correctly. Does not clone functions or non-serializable objects. - * - * Replaces the common `JSON.parse(JSON.stringify(obj))` pattern, which loses - * type information (Dates become strings, undefined values are dropped). - */ -export function deepClone(value: T): T { - return structuredClone(value) -} - /** * Returns a new object with the given keys removed. * diff --git a/scripts/check-utils-enforcement.ts b/scripts/check-utils-enforcement.ts index 2967b615a6e..22565e4c781 100644 --- a/scripts/check-utils-enforcement.ts +++ b/scripts/check-utils-enforcement.ts @@ -62,7 +62,7 @@ const BANNED_PATTERNS: Array<{ { pattern: /JSON\.parse\s*\(\s*JSON\.stringify\s*\(/g, description: 'JSON.parse(JSON.stringify(...))', - suggestion: 'deepClone() from @sim/utils/object', + suggestion: 'structuredClone() — built-in, no import needed', }, // Inline error message extraction (excludes null/undefined/false fallbacks — those have different semantics) { From aae060109e2840700dd62f498ca5886a91cc8c1e Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 15 May 2026 17:02:43 -0700 Subject: [PATCH 3/6] fix(utils): add missing biome noRestrictedImports rule and correct truncate docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- biome.json | 11 ++++++++++- packages/utils/src/string.ts | 13 +++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/biome.json b/biome.json index 19d6eea37d3..3346ec1a477 100644 --- a/biome.json +++ b/biome.json @@ -113,7 +113,16 @@ "noUnusedTemplateLiteral": "off", "useNumberNamespace": "error", "noInferrableTypes": "error", - "noUselessElse": "error" + "noUselessElse": "error", + "noRestrictedImports": { + "level": "error", + "options": { + "paths": { + "nanoid": "Use generateId() or generateShortId() from @sim/utils/id instead", + "uuid": "Use generateId() from @sim/utils/id instead" + } + } + } }, "complexity": { "noForEach": "off", diff --git a/packages/utils/src/string.ts b/packages/utils/src/string.ts index 217b1584d48..060419841ff 100644 --- a/packages/utils/src/string.ts +++ b/packages/utils/src/string.ts @@ -1,12 +1,13 @@ /** - * Truncates `str` to at most `maxLength` characters, appending `suffix` when - * truncation occurs. Defaults to `'...'`. + * 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...' - * truncate('hello world', 8, ' …') // 'hello …' + * truncate('hello world', 8) // 'hello wo...' (11 chars) + * truncate('hello world', 8, ' …') // 'hello wo …' * truncate('hi', 10) // 'hi' */ -export function truncate(str: string, maxLength: number, suffix = '...'): string { - return str.length > maxLength ? str.slice(0, maxLength) + suffix : str +export function truncate(str: string, sliceLength: number, suffix = '...'): string { + return str.length > sliceLength ? str.slice(0, sliceLength) + suffix : str } From a59f00919aebe84ea5b948cfde8eb9c0f95ed821 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 15 May 2026 17:03:58 -0700 Subject: [PATCH 4/6] 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. --- apps/sim/executor/handlers/workflow/workflow-handler.ts | 1 + apps/sim/hooks/use-execution-stream.ts | 1 + apps/sim/lib/copilot/request/go/stream.ts | 1 + apps/sim/tools/index.ts | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 656a241eabb..a96d2a611aa 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' diff --git a/apps/sim/hooks/use-execution-stream.ts b/apps/sim/hooks/use-execution-stream.ts index a9636527734..24a6e0cad4a 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, diff --git a/apps/sim/lib/copilot/request/go/stream.ts b/apps/sim/lib/copilot/request/go/stream.ts index 8ebd865df03..45539d74021 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, diff --git a/apps/sim/tools/index.ts b/apps/sim/tools/index.ts index 4ec92262363..090b30c628c 100644 --- a/apps/sim/tools/index.ts +++ b/apps/sim/tools/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 { sleep } from '@sim/utils/helpers' import { randomFloat } from '@sim/utils/random' import { getBYOKKey } from '@/lib/api-key/byok' From e1864cbcbf0ce8555fa00725d0b25d3e346715df Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 15 May 2026 17:21:02 -0700 Subject: [PATCH 5/6] fix(utils): fix build errors from getErrorMessage sweep and retry.ts Turbopack issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- apps/sim/app/api/tools/ssh/execute-command/route.ts | 1 + apps/sim/app/api/tools/ssh/execute-script/route.ts | 1 + apps/sim/app/api/tools/ssh/get-system-info/route.ts | 1 + apps/sim/app/api/tools/ssh/list-directory/route.ts | 1 + apps/sim/app/api/tools/ssh/move-rename/route.ts | 1 + .../components/sub-block/hooks/use-sub-block-value.ts | 2 +- apps/sim/executor/utils/json.ts | 1 + apps/sim/hooks/queries/mcp.ts | 1 + apps/sim/hooks/queries/providers.ts | 1 + apps/sim/lib/billing/client/upgrade.ts | 1 + apps/sim/lib/billing/credits/purchase.ts | 1 + apps/sim/lib/billing/organizations/membership.ts | 1 + apps/sim/lib/billing/stripe-payment-method.ts | 1 + apps/sim/lib/billing/webhooks/outbox-handlers.ts | 1 + apps/sim/lib/copilot/request/lifecycle/headless.ts | 1 + apps/sim/lib/copilot/request/lifecycle/start.ts | 1 + apps/sim/lib/environment/utils.ts | 1 + apps/sim/lib/messaging/email/mailer.ts | 1 + apps/sim/lib/messaging/sms/service.ts | 1 + apps/sim/lib/mothership/inbox/executor.ts | 1 + apps/sim/lib/mothership/inbox/response.ts | 1 + apps/sim/providers/deepseek/index.ts | 2 +- apps/sim/providers/fireworks/index.ts | 2 +- apps/sim/providers/gemini/core.ts | 2 +- apps/sim/providers/groq/index.ts | 2 +- apps/sim/providers/mistral/index.ts | 2 +- apps/sim/providers/ollama/index.ts | 2 +- apps/sim/providers/openai/core.ts | 2 +- apps/sim/providers/openrouter/index.ts | 2 +- apps/sim/providers/vllm/index.ts | 2 +- apps/sim/providers/xai/index.ts | 2 +- apps/sim/tools/tinybird/query.ts | 2 +- packages/utils/src/retry.ts | 6 +++--- 33 files changed, 35 insertions(+), 15 deletions(-) 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 26b52ff27aa..3b192957d97 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' 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 673f6e3897b..ce6cf309c71 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' 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 a350059fd35..9dc315afeba 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' 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 4407fda5710..b8d4b795619 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' 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 f61c8fb3551..2433bdd08f4 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' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts index ca41082f777..d32f3285c6c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value.ts @@ -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/executor/utils/json.ts b/apps/sim/executor/utils/json.ts index 02927bda5e4..8890e7cbb88 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') diff --git a/apps/sim/hooks/queries/mcp.ts b/apps/sim/hooks/queries/mcp.ts index 93d06c0814a..5601952d2b5 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' diff --git a/apps/sim/hooks/queries/providers.ts b/apps/sim/hooks/queries/providers.ts index a8f8570e8b8..c7eaabf5035 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 { diff --git a/apps/sim/lib/billing/client/upgrade.ts b/apps/sim/lib/billing/client/upgrade.ts index 70e6a339427..bbd0b5f8fe7 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' diff --git a/apps/sim/lib/billing/credits/purchase.ts b/apps/sim/lib/billing/credits/purchase.ts index 53913192346..e8dc21cd6f1 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' diff --git a/apps/sim/lib/billing/organizations/membership.ts b/apps/sim/lib/billing/organizations/membership.ts index 32a024ff6da..df63b8bf29c 100644 --- a/apps/sim/lib/billing/organizations/membership.ts +++ b/apps/sim/lib/billing/organizations/membership.ts @@ -20,6 +20,7 @@ import { 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, isNull, ne, or, sql } from 'drizzle-orm' import { syncUsageLimitsFromSubscription } from '@/lib/billing/core/usage' diff --git a/apps/sim/lib/billing/stripe-payment-method.ts b/apps/sim/lib/billing/stripe-payment-method.ts index d88c6c858ec..1c0e9dc7988 100644 --- a/apps/sim/lib/billing/stripe-payment-method.ts +++ b/apps/sim/lib/billing/stripe-payment-method.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import type Stripe from 'stripe' const logger = createLogger('StripePaymentMethod') diff --git a/apps/sim/lib/billing/webhooks/outbox-handlers.ts b/apps/sim/lib/billing/webhooks/outbox-handlers.ts index 830c205f564..b08a55050c9 100644 --- a/apps/sim/lib/billing/webhooks/outbox-handlers.ts +++ b/apps/sim/lib/billing/webhooks/outbox-handlers.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { member, subscription as subscriptionTable, user } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { and, eq } from 'drizzle-orm' import { isTeam } from '@/lib/billing/plan-helpers' import { requireStripeClient } from '@/lib/billing/stripe-client' diff --git a/apps/sim/lib/copilot/request/lifecycle/headless.ts b/apps/sim/lib/copilot/request/lifecycle/headless.ts index 038aa6e483d..0ae8fc75f55 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 { diff --git a/apps/sim/lib/copilot/request/lifecycle/start.ts b/apps/sim/lib/copilot/request/lifecycle/start.ts index e6f9a550d02..595efe15f33 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 { diff --git a/apps/sim/lib/environment/utils.ts b/apps/sim/lib/environment/utils.ts index cc3d1461e41..509dcfd7667 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' diff --git a/apps/sim/lib/messaging/email/mailer.ts b/apps/sim/lib/messaging/email/mailer.ts index 8ef9dc25ac6..06f70869146 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' diff --git a/apps/sim/lib/messaging/sms/service.ts b/apps/sim/lib/messaging/sms/service.ts index 65a45e08ccd..e8ef363d43e 100644 --- a/apps/sim/lib/messaging/sms/service.ts +++ b/apps/sim/lib/messaging/sms/service.ts @@ -1,4 +1,5 @@ import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' import { Twilio } from 'twilio' import { env } from '@/lib/core/config/env' diff --git a/apps/sim/lib/mothership/inbox/executor.ts b/apps/sim/lib/mothership/inbox/executor.ts index a6be94f7148..9f22e9df0d5 100644 --- a/apps/sim/lib/mothership/inbox/executor.ts +++ b/apps/sim/lib/mothership/inbox/executor.ts @@ -1,5 +1,6 @@ import { copilotChats, db, mothershipInboxTask, permissions, user, workspace } from '@sim/db' 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 { resolveOrCreateChat } from '@/lib/copilot/chat/lifecycle' diff --git a/apps/sim/lib/mothership/inbox/response.ts b/apps/sim/lib/mothership/inbox/response.ts index c521f5fe4cc..f289ec705ce 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' diff --git a/apps/sim/providers/deepseek/index.ts b/apps/sim/providers/deepseek/index.ts index d49a3383611..4f9c641291d 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' diff --git a/apps/sim/providers/fireworks/index.ts b/apps/sim/providers/fireworks/index.ts index 9f20af63484..8eb4e70e890 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' diff --git a/apps/sim/providers/gemini/core.ts b/apps/sim/providers/gemini/core.ts index 78c4780a44f..577b46454fd 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 { diff --git a/apps/sim/providers/groq/index.ts b/apps/sim/providers/groq/index.ts index e1decf9c4e7..149a5dfb6d5 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' diff --git a/apps/sim/providers/mistral/index.ts b/apps/sim/providers/mistral/index.ts index 47c24a2a8c4..1caa08bcf8f 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' diff --git a/apps/sim/providers/ollama/index.ts b/apps/sim/providers/ollama/index.ts index 545dda87e11..085cf967c59 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' diff --git a/apps/sim/providers/openai/core.ts b/apps/sim/providers/openai/core.ts index f623a396f90..11a465f2abc 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' diff --git a/apps/sim/providers/openrouter/index.ts b/apps/sim/providers/openrouter/index.ts index 7cc2412b6ff..1f897ffbc7a 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' diff --git a/apps/sim/providers/vllm/index.ts b/apps/sim/providers/vllm/index.ts index d1e9d318231..85756e47310 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' diff --git a/apps/sim/providers/xai/index.ts b/apps/sim/providers/xai/index.ts index ee73860997e..6f33bad536c 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' diff --git a/apps/sim/tools/tinybird/query.ts b/apps/sim/tools/tinybird/query.ts index 13092b40760..5a64a36fc63 100644 --- a/apps/sim/tools/tinybird/query.ts +++ b/apps/sim/tools/tinybird/query.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 { TinybirdQueryParams, TinybirdQueryResponse } from '@/tools/tinybird/types' import type { ToolConfig } from '@/tools/types' diff --git a/packages/utils/src/retry.ts b/packages/utils/src/retry.ts index 7b842c996e5..cc32d8c83e5 100644 --- a/packages/utils/src/retry.ts +++ b/packages/utils/src/retry.ts @@ -1,5 +1,3 @@ -import { randomFloat } from './random.js' - /** Default retry pacing: 500 ms floor, 30 s ceiling. */ const DEFAULT_BACKOFF_BASE_MS = 500 const DEFAULT_BACKOFF_MAX_MS = 30_000 @@ -29,7 +27,9 @@ export function backoffWithJitter( return Math.min(Math.max(retryAfterMs, baseMs), maxMs) } const exponential = Math.min(baseMs * 2 ** (attempt - 1), maxMs) - return exponential * (0.8 + randomFloat() * 0.4) + // 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. */ From cbbf9721597030c0f67d4e8318e69c675f756822 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 15 May 2026 17:24:34 -0700 Subject: [PATCH 6/6] 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. --- apps/sim/app/api/tools/crowdstrike/query/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/sim/app/api/tools/crowdstrike/query/route.ts b/apps/sim/app/api/tools/crowdstrike/query/route.ts index 538bcc653e6..92335d15c5e 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 = getErrorMessage(error, 'Unknown error') + const message = toError(error).message logger.error(`[${requestId}] CrowdStrike request failed`, { error: message }) return NextResponse.json({ success: false, error: message }, { status: 500 }) }