From 27dde7a4d69bdd4e5c5830005fac4f493541d9ad Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 1 Jun 2026 01:12:00 +0100 Subject: [PATCH] chore(lint): enable stricter error rules --- .oxlintrc.json | 6 +++ extensions/acpx/src/runtime-turn.ts | 16 +++++- extensions/acpx/src/runtime.test.ts | 9 +++- extensions/active-memory/index.test.ts | 26 ++++++++- extensions/active-memory/index.ts | 3 +- extensions/bonjour/src/advertiser.ts | 4 +- .../src/browser/cdp.helpers.internal.test.ts | 17 +++++- extensions/browser/src/browser/chrome-mcp.ts | 27 ++++++++-- .../browser/src/browser/client-fetch.ts | 26 ++++++++- extensions/browser/src/browser/pw-session.ts | 18 ++++++- .../src/browser/pw-tools-core.interactions.ts | 28 +++++++--- .../browser/src/browser/routes/agent.act.ts | 16 +++++- .../src/browser/routes/agent.snapshot.ts | 2 +- .../browser/routes/dispatcher.abort.test.ts | 29 +++++++++- extensions/browser/src/browser/screenshot.ts | 16 +++++- .../src/browser/server-context.tab-ops.ts | 2 +- .../src/node-host/invoke-browser.test.ts | 19 ++++++- extensions/browser/src/sdk-node-runtime.ts | 21 +++++++- extensions/browser/src/server.ts | 2 +- .../byteplus/video-generation-provider.ts | 2 - extensions/canvas/scripts/bundle-a2ui.mjs | 8 +-- extensions/canvas/scripts/copy-a2ui.mjs | 10 ++-- extensions/canvas/src/host/server.ts | 2 +- extensions/codex-supervisor/src/mcp-serve.ts | 2 +- .../src/app-server/native-subagent-monitor.ts | 4 +- .../app-server/plugin-approval-roundtrip.ts | 18 ++++++- .../run-attempt.context-engine.test.ts | 16 +++++- .../codex/src/app-server/run-attempt.test.ts | 6 +-- .../codex/src/app-server/user-input-bridge.ts | 8 +-- extensions/codex/src/migration/source.ts | 34 ++++++------ extensions/copilot/src/attempt.test.ts | 18 ++++++- extensions/copilot/src/event-bridge.ts | 16 +++++- extensions/copilot/src/runtime.test.ts | 16 +++++- extensions/copilot/src/sdk-loader.ts | 2 +- .../copilot/src/telemetry-bridge.test.ts | 16 +++++- extensions/device-pair/notify.ts | 4 +- extensions/diffs/src/browser.ts | 4 +- extensions/diffs/src/store.ts | 2 +- extensions/discord/src/client.proxy.test.ts | 16 +++++- extensions/discord/src/components.parse.ts | 1 - extensions/discord/src/monitor.gateway.ts | 16 +++++- .../discord/src/monitor/ack-reactions.ts | 2 +- .../discord/src/monitor/gateway-plugin.ts | 4 +- extensions/discord/src/monitor/listeners.ts | 4 +- .../discord/src/monitor/message-handler.ts | 2 +- .../discord/src/monitor/message-media.ts | 4 +- .../src/monitor/provider.lifecycle.test.ts | 18 ++++++- extensions/discord/src/voice/audio.ts | 2 +- extensions/discord/src/voice/manager.ts | 22 +++++--- .../document-extract/document-extractor.ts | 2 +- extensions/feishu/src/bot-content.ts | 1 - extensions/feishu/src/bot.ts | 2 +- extensions/feishu/src/conversation-id.ts | 1 - extensions/feishu/src/monitor.account.ts | 4 +- .../feishu/src/monitor.bot-menu-handler.ts | 4 +- .../src/monitor.comment-notice-handler.ts | 2 +- extensions/feishu/src/monitor.comment.ts | 4 +- .../feishu/src/monitor.message-handler.ts | 2 +- .../src/monitor.webhook-security.test.ts | 4 +- extensions/feishu/src/session-conversation.ts | 1 - extensions/feishu/src/streaming-card.ts | 4 +- extensions/google-meet/src/agent-consult.ts | 2 +- extensions/google-meet/src/realtime-node.ts | 6 +-- extensions/google-meet/src/realtime.ts | 2 +- .../google-meet/src/voice-call-gateway.ts | 2 +- extensions/google/google.live.test.ts | 16 +++++- extensions/google/transport-stream.test.ts | 21 +++++++- extensions/googlechat/src/monitor-webhook.ts | 2 +- .../src/monitor.watch-subscribe-retry.test.ts | 2 +- .../imessage/src/monitor/monitor-provider.ts | 2 +- extensions/irc/src/client.ts | 2 +- extensions/line/src/bot-handlers.ts | 16 +++++- extensions/line/src/monitor.ts | 2 +- extensions/line/src/send.ts | 3 +- extensions/line/src/webhook-node.ts | 2 +- extensions/line/src/webhook.ts | 2 +- extensions/lmstudio/src/stream.ts | 2 +- extensions/lobster/src/lobster-runner.test.ts | 21 +++++++- extensions/lobster/src/lobster-runner.ts | 18 ++++++- .../src/matrix/client/file-sync-store.ts | 2 +- .../matrix/src/matrix/monitor/events.test.ts | 2 +- .../matrix/src/matrix/monitor/events.ts | 2 +- .../matrix/src/matrix/monitor/handler.ts | 18 ++++--- .../src/matrix/monitor/inbound-dedupe.ts | 2 +- extensions/matrix/src/matrix/monitor/index.ts | 2 +- .../src/matrix/monitor/reaction-events.ts | 14 ++--- .../src/matrix/monitor/reply-context.ts | 14 ++--- .../matrix/src/matrix/monitor/task-runner.ts | 2 +- .../src/matrix/monitor/thread-context.ts | 2 +- .../src/matrix/monitor/verification-events.ts | 2 +- .../sdk/idb-persistence.test-helpers.ts | 50 ++++++++++++++--- .../matrix/src/matrix/sdk/idb-persistence.ts | 28 ++++++++-- .../src/matrix/sdk/verification-manager.ts | 6 +-- extensions/matrix/src/matrix/startup-abort.ts | 18 ++++++- .../matrix/src/matrix/thread-bindings.ts | 6 +-- .../mattermost/src/mattermost/monitor.ts | 2 +- .../memory-core/src/dreaming-narrative.ts | 4 +- extensions/memory-core/src/dreaming-repair.ts | 5 +- extensions/memory-core/src/dreaming.ts | 4 +- .../src/memory/manager-async-state.ts | 2 +- .../memory/manager-embedding-timeout.test.ts | 20 ++++++- .../src/memory/manager-sync-ops.ts | 10 ++-- extensions/memory-core/src/memory/manager.ts | 26 ++++++--- .../memory-core/src/memory/qmd-manager.ts | 16 +++--- .../memory-core/src/memory/search-manager.ts | 2 +- .../memory-core/src/short-term-promotion.ts | 2 +- extensions/memory-lancedb/index.ts | 4 +- extensions/memory-lancedb/lancedb-runtime.ts | 2 +- extensions/memory-wiki/src/import-runs.ts | 5 +- .../minimax/video-generation-provider.ts | 2 - extensions/moonshot/moonshot.live.test.ts | 16 +++++- extensions/msteams/src/feedback-invoke.ts | 2 +- .../src/monitor-handler/message-handler.ts | 4 +- extensions/msteams/src/monitor.ts | 16 +++++- extensions/msteams/src/reply-dispatcher.ts | 4 +- extensions/nostr/src/nostr-bus.ts | 6 +-- extensions/ollama/src/setup.ts | 18 ++++++- .../openai-chatgpt-oauth-flow.runtime.ts | 18 ++++++- extensions/qa-channel/src/bus-client.ts | 16 +++++- extensions/qa-lab/src/docker-harness.ts | 16 +++++- extensions/qa-lab/src/gateway-child.ts | 26 +++++++-- .../slack/slack-live.runtime.ts | 10 ++-- .../whatsapp/whatsapp-live.runtime.ts | 2 +- .../qa-lab/src/providers/server-runtime.ts | 1 - .../qa-lab/src/suite-runtime-gateway.ts | 19 ++++++- extensions/qa-lab/src/suite.ts | 2 +- extensions/qa-matrix/src/cli.runtime.ts | 18 ++++++- .../runners/contract/scenario-runtime-cli.ts | 2 +- extensions/qqbot/src/engine/api/token.ts | 6 ++- .../src/engine/gateway/interaction-handler.ts | 2 +- .../qqbot/src/engine/gateway/message-queue.ts | 4 +- .../src/engine/messaging/streaming-c2c.ts | 4 +- extensions/qqbot/src/engine/utils/audio.ts | 2 +- .../runway/video-generation-provider.ts | 3 -- extensions/signal/src/client-container.ts | 16 +++++- extensions/signal/src/client.ts | 18 ++++++- .../src/monitor.tool-result.autostart.test.ts | 24 ++++++++- extensions/signal/src/monitor.ts | 2 +- extensions/slack/src/monitor/media.ts | 2 +- .../src/monitor/message-handler/dispatch.ts | 20 +++++-- .../src/monitor/message-handler/prepare.ts | 2 +- .../slack/src/monitor/provider-support.ts | 28 ++++++++-- .../src/monitor/provider.reconnect.test.ts | 4 +- .../telegram/src/bot-handlers.runtime.ts | 2 +- .../telegram/src/bot-message-context.ts | 6 +-- .../telegram/src/bot-message-dispatch.ts | 2 +- extensions/telegram/src/bot-message.ts | 2 +- .../telegram/src/bot-native-command-menu.ts | 2 +- extensions/telegram/src/bot-update-tracker.ts | 6 +-- .../telegram/src/bot.fetch-abort.test.ts | 34 ++++++++++-- extensions/telegram/src/monitor.test.ts | 16 +++++- .../telegram/src/polling-session.test.ts | 16 +++++- extensions/telegram/src/polling-session.ts | 2 +- .../telegram/src/polling-transport-state.ts | 2 +- extensions/telegram/src/probe.ts | 19 ++++++- extensions/telegram/src/send.proxy.test.ts | 20 ++++++- extensions/telegram/src/send.ts | 4 +- .../src/telegram-ingress-worker.runtime.ts | 2 +- extensions/telegram/src/thread-bindings.ts | 2 +- extensions/telegram/src/webhook.test.ts | 2 +- extensions/telegram/src/webhook.ts | 4 +- extensions/tlon/src/monitor/index.ts | 6 ++- extensions/tlon/src/urbit/sse-client.ts | 4 +- extensions/twitch/src/monitor.ts | 2 +- extensions/voice-call/index.ts | 2 +- .../src/gateway-continue-operation.ts | 2 +- extensions/voice-call/src/manager.ts | 4 +- extensions/voice-call/src/manager/events.ts | 4 +- .../voice-call/src/manager/timers.test.ts | 4 +- extensions/voice-call/src/providers/twilio.ts | 1 - extensions/voice-call/src/webhook.ts | 11 ++-- .../src/webhook/realtime-handler.ts | 2 +- .../src/webhook/stale-call-reaper.ts | 2 +- ...o-reply.connection-and-logging.e2e.test.ts | 16 +++++- extensions/whatsapp/src/auto-reply/monitor.ts | 4 +- .../src/auto-reply/monitor/last-route.ts | 2 +- .../whatsapp/src/connection-controller.ts | 2 +- extensions/whatsapp/src/creds-persistence.ts | 2 +- extensions/whatsapp/src/inbound/media.ts | 2 +- extensions/whatsapp/src/inbound/monitor.ts | 6 +-- extensions/whatsapp/src/login-qr.ts | 4 +- extensions/whatsapp/src/login.ts | 2 +- extensions/whatsapp/src/session.ts | 23 +++++++- extensions/xai/video-generation-provider.ts | 2 - extensions/zalo/src/monitor.webhook.ts | 2 +- extensions/zalouser/src/monitor.ts | 4 +- packages/agent-core/src/agent-loop.ts | 4 +- .../agent-core/src/harness/agent-harness.ts | 16 +++++- packages/agent-core/src/harness/types.ts | 16 +++++- packages/gateway-client/src/client.ts | 2 +- .../src/read-response-with-limit.ts | 18 ++++++- .../src/host/embeddings-worker.ts | 21 +++++++- .../src/host/embeddings.test.ts | 2 +- .../memory-host-sdk/src/host/embeddings.ts | 16 +++++- .../memory-host-sdk/src/host/retry-utils.ts | 18 ++++++- scripts/anthropic-prompt-probe.ts | 2 +- scripts/bench-gateway-restart.ts | 2 +- scripts/bench-gateway-startup.ts | 2 +- scripts/check-changelog-attributions.mjs | 10 ++-- scripts/check-dependency-pins.mjs | 10 ++-- scripts/check-docs-mdx.mjs | 10 ++-- .../check-extension-package-tsc-boundary.mjs | 35 ++++++++---- scripts/check-gateway-cpu-scenarios.mjs | 10 ++-- scripts/check-memory-fd-repro.mjs | 14 ++--- scripts/check-no-conflict-markers.mjs | 15 +++--- scripts/check-package-patches.mjs | 10 ++-- scripts/check-plugin-gateway-gauntlet.mjs | 12 +++-- scripts/check-plugin-sdk-subpath-exports.mjs | 10 ++-- scripts/control-ui-i18n.ts | 20 +++++-- scripts/debug-claude-usage.ts | 2 +- scripts/dependency-changes-report.mjs | 4 +- .../dependency-ownership-surface-report.mjs | 2 +- scripts/dependency-vulnerability-gate.mjs | 2 +- .../dev/discord-acp-plain-language-smoke.ts | 4 +- scripts/dev/ios-node-e2e.ts | 2 +- scripts/dev/realtime-talk-live-smoke.ts | 20 +++++-- scripts/dev/tui-pty-test-watch.ts | 2 +- .../e2e/crestodian-first-run-docker-client.ts | 2 +- .../e2e/crestodian-planner-docker-client.mjs | 10 ++-- .../e2e/crestodian-rescue-docker-client.ts | 2 +- scripts/e2e/kitchen-sink-rpc-walk.mjs | 21 +++++++- .../runtime-smoke.mjs | 29 ++++++++-- scripts/e2e/lib/clawhub-fixture-server.cjs | 10 ++-- scripts/e2e/lib/codex-media-path/client.mjs | 16 +++++- .../lib/openai-web-search-minimal/client.mjs | 16 +++++- scripts/e2e/mcp-channels-docker-client.ts | 4 +- scripts/e2e/npm-telegram-live-runner.ts | 2 +- scripts/e2e/openwebui-probe.mjs | 10 ++-- scripts/e2e/secret-provider-integrations.mjs | 20 +++++-- scripts/embedded-run-abort-leak.ts | 24 +++++++-- scripts/firecrawl-compare.ts | 2 +- .../generate-dependency-release-evidence.mjs | 2 +- scripts/github/dependency-guard.mjs | 10 ++-- scripts/github/real-behavior-proof-check.mjs | 19 ++++++- scripts/lib/bounded-response.mjs | 19 ++++++- scripts/lib/openclaw-test-state.mjs | 12 +++-- scripts/openclaw-cross-os-release-checks.ts | 19 ++++++- .../openclaw-performance-source-summary.mjs | 10 ++-- scripts/package-openclaw-for-docker.mjs | 26 +++++++-- ...proof-73706-message-sending-session-key.ts | 2 +- scripts/protocol-gen-swift.ts | 2 +- scripts/protocol-gen.ts | 2 +- scripts/proxy-install-ca.mjs | 10 ++-- scripts/qa-otel-smoke.ts | 2 +- scripts/readability-basic-compare.ts | 2 +- scripts/release-candidate-checklist.mjs | 10 ++-- .../resolve-openclaw-package-candidate.mjs | 38 +++++++------ scripts/rtt.ts | 2 +- scripts/run-node.mjs | 10 ++-- scripts/secrets/openclaw-bws-resolver.mjs | 12 +++-- scripts/sync-moonshot-docs.ts | 2 +- scripts/test-docker-all.mjs | 10 ++-- scripts/test-group-report.mjs | 10 ++-- scripts/test-live-media.ts | 2 +- scripts/test-projects.mjs | 12 +++-- scripts/test-shell-completion.ts | 2 +- scripts/transitive-manifest-risk-report.mjs | 4 +- scripts/verify-docker-attestations.mjs | 10 ++-- .../verify-plugin-npm-published-runtime.mjs | 14 ++--- scripts/watch-node.mjs | 40 ++++++++++---- scripts/zai-fallback-repro.ts | 2 +- security/opengrep/compile-rules.mjs | 10 ++-- src/acp/control-plane/manager.core.ts | 16 +++++- .../manager.initialize-session.ts | 2 +- .../manager.runtime-resume-state.ts | 16 +++++- src/acp/control-plane/manager.test-helpers.ts | 2 +- src/acp/control-plane/manager.test.ts | 2 +- src/acp/control-plane/manager.turn-timeout.ts | 4 +- src/acp/control-plane/manager.utils.ts | 16 +++++- src/acp/control-plane/spawn.ts | 6 +-- src/acp/server.ts | 4 +- .../translator.prompt-harness.test-support.ts | 2 +- src/acp/translator.ts | 4 +- src/agents/agent-bundle-mcp-runtime.ts | 18 ++++++- src/agents/agent-tools-agent-config.test.ts | 4 +- .../agent-tools.before-tool-call.e2e.test.ts | 14 ++++- src/agents/agent-tools.before-tool-call.ts | 25 +++++++-- src/agents/agent-tools.read.ts | 19 ++++++- src/agents/anthropic-transport-stream.ts | 16 +++++- src/agents/api-key-rotation.ts | 16 +++++- src/agents/apply-patch-update.ts | 5 +- .../auth-profiles/oauth-refresh-queue.test.ts | 4 +- src/agents/bash-tools.exec-host-shared.ts | 2 +- src/agents/bash-tools.exec-runtime.ts | 2 +- src/agents/bash-tools.exec.ts | 2 +- src/agents/cli-runner.ts | 2 +- src/agents/cli-runner/claude-live-session.ts | 2 +- src/agents/command/delivery.ts | 16 +++++- src/agents/compaction-planning-worker.ts | 32 ++++++++++- .../context-engine-maintenance.ts | 4 +- src/agents/embedded-agent-runner/run.ts | 20 +++++-- .../embedded-agent-runner/run/abortable.ts | 18 ++++++- .../run/attempt-abort.ts | 2 +- .../run/attempt.async-tasks.ts | 18 ++++++- .../run/attempt.model-diagnostic-events.ts | 2 +- .../run/attempt.queue-message.ts | 16 +++++- .../run/attempt.session-lock.ts | 20 ++++++- .../run/attempt.sessions-yield.ts | 2 +- .../run/attempt.stop-reason-recovery.ts | 2 +- .../run/attempt.subscription-cleanup.ts | 18 ++++++- .../embedded-agent-runner/run/attempt.ts | 20 +++++-- .../run/auth-controller.ts | 2 +- .../run/llm-idle-timeout.test.ts | 16 +++++- .../run/llm-idle-timeout.ts | 21 +++++++- ...ded-agent-subscribe.handlers.compaction.ts | 6 +-- ...dded-agent-subscribe.handlers.lifecycle.ts | 4 +- ...edded-agent-subscribe.handlers.messages.ts | 4 +- ...embedded-agent-subscribe.handlers.tools.ts | 2 +- .../embedded-agent-subscribe.handlers.ts | 4 +- src/agents/embedded-agent-subscribe.ts | 4 +- src/agents/harness/lifecycle-hook-helpers.ts | 16 +++--- src/agents/harness/native-hook-relay.ts | 20 +++++-- .../harness/prompt-compaction-hook-helpers.ts | 4 +- src/agents/main-session-restart-recovery.ts | 2 +- src/agents/model-fallback.ts | 18 ++++++- src/agents/model-provider-auth.ts | 16 +++++- src/agents/provider-local-service.ts | 16 +++++- src/agents/run-cleanup-timeout.ts | 4 +- src/agents/sandbox/docker-backend.ts | 2 - src/agents/sandbox/registry.ts | 2 +- src/agents/sandbox/ssh.ts | 16 +++++- src/agents/sessions/agent-session.ts | 4 +- src/agents/sessions/settings-manager.ts | 2 +- src/agents/sessions/tools/bash.ts | 18 ++++++- src/agents/sessions/tools/ls.ts | 16 +++++- src/agents/sessions/tools/read.ts | 16 +++++- src/agents/subagent-announce.live.test.ts | 30 ++++++++--- src/agents/subagent-orphan-recovery.ts | 2 +- src/agents/subagent-registry-lifecycle.ts | 10 ++-- src/agents/subagent-registry-run-manager.ts | 2 +- src/agents/subagent-registry.ts | 2 +- src/agents/tool-images.ts | 16 +++++- .../tools/media-generate-background-shared.ts | 2 +- src/agents/tools/transcripts-tool.ts | 4 +- .../reply/agent-runner-execution.ts | 2 +- src/auto-reply/reply/block-reply-pipeline.ts | 2 +- src/auto-reply/reply/dispatch-acp-delivery.ts | 2 +- src/auto-reply/reply/followup-runner.ts | 2 +- src/auto-reply/reply/reply-delivery.ts | 2 +- src/auto-reply/reply/reply-dispatcher.ts | 2 +- src/auto-reply/reply/reply-media-paths.ts | 2 +- src/auto-reply/reply/session-updates.ts | 4 +- src/channels/ack-reactions.ts | 20 +++++-- src/channels/plugins/binding-routing.ts | 2 +- src/channels/typing.ts | 2 +- src/cli/daemon-cli/status.gather.ts | 4 +- src/cli/gateway-cli/qa-parent-watchdog.ts | 2 +- src/cli/gateway-cli/run-loop.ts | 10 ++-- src/cli/gateway-cli/run.ts | 2 +- src/cli/node-cli/daemon.ts | 2 +- src/cli/nodes-camera.ts | 16 +++++- src/cli/pairing-cli.ts | 2 +- src/cli/program/build-program.test.ts | 2 +- src/cli/prompt.ts | 16 +++++- src/cli/security-cli.ts | 4 +- src/commands/chutes-oauth.ts | 16 +++++- src/commands/configure.daemon.ts | 2 +- src/commands/models.list.e2e.test.ts | 20 +++++-- src/commands/status-runtime-shared.ts | 2 +- src/commitments/runtime.ts | 2 +- src/cron/service/failure-alerts.ts | 2 +- src/cron/service/ops.ts | 2 +- src/cron/service/timer.ts | 6 +-- src/gateway/call.test.ts | 24 +++++---- src/gateway/call.ts | 2 +- src/gateway/exec-approval-ios-push.ts | 2 +- src/gateway/gateway-acp-bind.live.test.ts | 2 +- .../gateway-acp-spawn-defaults.live.test.ts | 2 +- .../gateway-cli-backend.live-helpers.ts | 2 +- src/gateway/model-pricing-cache.test.ts | 16 +++++- src/gateway/probe.ts | 2 +- src/gateway/server-channels.ts | 2 +- src/gateway/server-control-ui-root.ts | 2 +- src/gateway/server-cron.ts | 4 +- src/gateway/server-discovery-runtime.ts | 2 +- src/gateway/server-http.ts | 4 +- src/gateway/server-maintenance.ts | 6 +-- src/gateway/server-methods/agent.test.ts | 19 ++++++- src/gateway/server-methods/agent.ts | 2 +- src/gateway/server-methods/channels.ts | 2 +- src/gateway/server-methods/chat.ts | 6 +-- src/gateway/server-methods/config.ts | 16 +++++- src/gateway/server-methods/exec-approval.ts | 4 +- src/gateway/server-methods/health.ts | 2 +- .../server-methods/models-auth-status.ts | 8 +-- src/gateway/server-methods/nodes.ts | 2 +- src/gateway/server-methods/plugin-approval.ts | 12 +++-- src/gateway/server-methods/tools-effective.ts | 18 ++++++- src/gateway/server-methods/usage.ts | 2 +- src/gateway/server-node-events.ts | 8 +-- src/gateway/server-reload-handlers.ts | 4 +- src/gateway/server-restart-sentinel.ts | 16 +++++- src/gateway/server-runtime-services.ts | 14 +++-- src/gateway/server-startup-memory.ts | 2 +- src/gateway/server-startup-post-attach.ts | 28 +++++----- src/gateway/server.impl.ts | 4 +- ...server.plugin-node-capability-auth.test.ts | 2 +- .../server/ws-connection/message-handler.ts | 12 ++--- src/gateway/session-reset-service.ts | 6 +-- src/gateway/sessions-history-http.ts | 2 +- .../tools-invoke-http.cron-regression.test.ts | 2 +- src/gateway/tools-invoke-http.test.ts | 2 +- src/hooks/fire-and-forget.ts | 4 +- src/index.ts | 2 +- src/infra/approval-handler-bootstrap.ts | 2 +- src/infra/clawhub.ts | 1 - .../exec-approval-channel-runtime.test.ts | 2 +- src/infra/exec-approval-forwarder.ts | 4 +- src/infra/net/fetch-guard.ssrf.test.ts | 18 ++++++- src/infra/npm-managed-root.ts | 5 +- .../outbound/session-binding-service.test.ts | 7 +-- src/infra/push-apns-http2.ts | 16 +++++- src/infra/push-apns.ts | 16 +++++- src/infra/retry.test.ts | 2 +- src/infra/retry.ts | 18 ++++++- src/infra/tailscale.ts | 19 ++++++- src/llm/providers/openai-chatgpt-responses.ts | 16 +++++- src/llm/providers/register-builtins.ts | 4 +- src/llm/utils/oauth/anthropic.ts | 18 ++++++- src/logging/diagnostic.ts | 4 +- src/mcp/openclaw-tools-serve.ts | 2 +- src/mcp/plugin-tools-serve.ts | 2 +- src/media-generation/runtime-shared.ts | 16 +++++- src/media/fetch.ts | 18 ++++++- src/media/ffmpeg-exec.ts | 16 +++++- src/media/store.ts | 19 +++++-- src/node-host/with-timeout.ts | 21 +++++++- src/plugin-sdk/provider-oauth-runtime.ts | 18 ++++++- src/plugin-sdk/qa-runtime.ts | 16 +++++- src/plugins/bundled-plugin-metadata.test.ts | 16 +++++- src/plugins/conversation-binding.ts | 2 +- src/plugins/host-hook-runtime.ts | 2 +- src/plugins/marketplace.ts | 18 ++++++- src/plugins/plugin-peer-link.ts | 19 ++++--- src/plugins/runtime.ts | 2 +- src/process/command-queue.ts | 2 +- src/process/spawn-utils.ts | 16 +++++- src/process/supervisor/adapters/child.ts | 18 ++++++- src/process/supervisor/supervisor.ts | 2 +- .../proxy-server.managed-proxy.test.ts | 6 +-- src/proxy-capture/runtime.ts | 2 +- src/security/audit-extra.async.ts | 6 +-- src/security/audit-plugins-trust.ts | 2 +- src/security/audit.ts | 2 +- src/security/fix.ts | 2 +- src/skills/lifecycle/upload-store.test.ts | 16 +++++- src/tui/tui-pty-test-support.ts | 16 +++++- src/tui/tui.ts | 4 +- src/utils/fetch-timeout.test.ts | 20 ++++++- test/proof/thinking-signature-real-proof.ts | 2 +- test/scripts/pnpm-audit-prod.test.ts | 27 ++++++++-- .../scripts/real-behavior-proof-check.test.ts | 18 ++++++- .../real-behavior-proof-policy.test.ts | 53 ++++++++++++------- ...resolve-openclaw-package-candidate.test.ts | 16 +++++- ui/src/ui/app-gateway.ts | 2 +- ui/src/ui/app.ts | 1 - ui/src/ui/chat/realtime-talk-shared.ts | 2 +- ui/src/ui/embed-sandbox.ts | 1 - 458 files changed, 3159 insertions(+), 936 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index ef2a2311347..a43d2938c73 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -82,7 +82,10 @@ "typescript/no-meaningless-void-operator": "error", "typescript/no-misused-promises": "error", "typescript/no-inferrable-types": "error", + "typescript/only-throw-error": "error", "typescript/no-non-null-asserted-nullish-coalescing": "error", + "typescript/prefer-promise-reject-errors": "error", + "typescript/restrict-plus-operands": "error", "typescript/no-unnecessary-qualifier": "error", "typescript/no-unnecessary-type-assertion": "error", "typescript/no-unnecessary-type-arguments": "error", @@ -109,6 +112,8 @@ "typescript/require-array-sort-compare": "error", "typescript/restrict-template-expressions": "error", "typescript/triple-slash-reference": "error", + "typescript/unbound-method": "error", + "typescript/use-unknown-in-catch-callback-variable": "error", "unicorn/consistent-date-clone": "error", "unicorn/consistent-empty-array-spread": "error", "unicorn/consistent-function-scoping": "off", @@ -128,6 +133,7 @@ "unicorn/no-unnecessary-slice-end": "error", "unicorn/no-useless-error-capture-stack-trace": "error", "unicorn/no-useless-promise-resolve-reject": "error", + "unicorn/no-useless-switch-case": "error", "unicorn/no-zero-fractions": "error", "unicorn/prefer-date-now": "error", "unicorn/prefer-dom-node-text-content": "error", diff --git a/extensions/acpx/src/runtime-turn.ts b/extensions/acpx/src/runtime-turn.ts index f190293d415..2a077ddefcc 100644 --- a/extensions/acpx/src/runtime-turn.ts +++ b/extensions/acpx/src/runtime-turn.ts @@ -68,7 +68,7 @@ class LegacyRunTurnEventQueue { return item; } if (this.error) { - throw this.error; + throw toLintErrorObject(this.error, "Non-Error thrown"); } if (this.closed) { return null; @@ -178,3 +178,17 @@ export function lazyStartRuntimeTurn( }, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/acpx/src/runtime.test.ts b/extensions/acpx/src/runtime.test.ts index 41e58ce186c..536e0c2f41b 100644 --- a/extensions/acpx/src/runtime.test.ts +++ b/extensions/acpx/src/runtime.test.ts @@ -286,7 +286,7 @@ describe("AcpxRuntime fresh reset wrapper", () => { }) .then( () => ({ status: "resolved" as const }), - (error) => ({ status: "rejected" as const, error }), + (error: unknown) => ({ status: "rejected" as const, error }), ); expect(outcome.status).toBe("rejected"); @@ -298,7 +298,12 @@ describe("AcpxRuntime fresh reset wrapper", () => { code: "ACP_SESSION_INIT_FAILED", message: expect.stringContaining("deployment missing"), }); - expect(outcome.error.message).not.toContain("sk-testsecret1234567890"); + const error = outcome.error; + expect(error).toBeInstanceOf(AcpRuntimeError); + if (!(error instanceof AcpRuntimeError)) { + throw new Error("expected AcpRuntimeError"); + } + expect(error.message).not.toContain("sk-testsecret1234567890"); }); it("adds Codex wrapper stderr tail to generic first-turn failures", async () => { diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts index 348a42ba8bc..0e69eff5845 100644 --- a/extensions/active-memory/index.test.ts +++ b/extensions/active-memory/index.test.ts @@ -218,13 +218,21 @@ describe("active-memory plugin", () => { }; const waitForAbort = async (abortSignal?: AbortSignal): Promise => { if (abortSignal?.aborted) { - throw (abortSignal.reason as unknown) ?? new Error("Operation aborted"); + throw toLintErrorObject( + (abortSignal.reason as unknown) ?? new Error("Operation aborted"), + "Non-Error thrown", + ); } return await new Promise((_resolve, reject) => { abortSignal?.addEventListener( "abort", () => { - reject((abortSignal.reason as unknown) ?? new Error("Operation aborted")); + reject( + toLintErrorObject( + (abortSignal.reason as unknown) ?? new Error("Operation aborted"), + "Non-Error rejection", + ), + ); }, { once: true }, ); @@ -4350,3 +4358,17 @@ describe("active-memory plugin", () => { expect(config.circuitBreakerCooldownMs).toBe(5000); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/active-memory/index.ts b/extensions/active-memory/index.ts index df9f503d992..028af904615 100644 --- a/extensions/active-memory/index.ts +++ b/extensions/active-memory/index.ts @@ -1011,7 +1011,6 @@ function buildPromptStyleLines(style: ActiveMemoryPromptStyle): string[] { "If relevant memory is mostly a stable user preference or recurring habit, lean toward returning it.", "If the strongest match is only a one-off historical fact and not a recurring preference or habit, prefer NONE unless the latest user message clearly asks for that fact.", ]; - case "balanced": default: return [ "Treat the latest user message as the primary query.", @@ -1982,7 +1981,7 @@ async function waitForSubagentPartialTimeoutData( (await Promise.race([ subagentPromise.then( () => undefined, - (error) => readPartialTimeoutData(error), + (error: unknown) => readPartialTimeoutData(error), ), timeoutPromise, ])) ?? {} diff --git a/extensions/bonjour/src/advertiser.ts b/extensions/bonjour/src/advertiser.ts index 5e0ddb6bd99..5686333543e 100644 --- a/extensions/bonjour/src/advertiser.ts +++ b/extensions/bonjour/src/advertiser.ts @@ -571,7 +571,7 @@ export async function startGatewayBonjourAdvertiser( .then(() => { logger.info(`bonjour: advertised ${serviceSummary(label, svc)}`); }) - .catch((err) => { + .catch((err: unknown) => { handleAdvertiseFailure(label, svc, err, "failed"); }); } catch (err) { @@ -747,7 +747,7 @@ export async function startGatewayBonjourAdvertiser( )})`, ); try { - void svc.advertise().catch((err) => { + void svc.advertise().catch((err: unknown) => { logger.warn( `bonjour: watchdog re-advertise failed (${serviceSummary(label, svc)}): ${formatBonjourError(err)}`, ); diff --git a/extensions/browser/src/browser/cdp.helpers.internal.test.ts b/extensions/browser/src/browser/cdp.helpers.internal.test.ts index 52f8dc6ea43..53e4d6dbb0a 100644 --- a/extensions/browser/src/browser/cdp.helpers.internal.test.ts +++ b/extensions/browser/src/browser/cdp.helpers.internal.test.ts @@ -416,7 +416,8 @@ describe("cdp.helpers internal", () => { await expect( withCdpSocket(server.url, async (send) => { await send("Test.ok"); - const rejectRawString = () => Promise.reject("raw-string-from-callback"); + const rejectRawString = () => + Promise.reject(toLintErrorObject("raw-string-from-callback", "Non-Error rejection")); return rejectRawString(); }), ).rejects.toThrow(/raw-string-from-callback/); @@ -572,3 +573,17 @@ describe("openCdpWebSocket option handling", () => { ws.close(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/chrome-mcp.ts b/extensions/browser/src/browser/chrome-mcp.ts index 09a63054d5c..156c540a6dc 100644 --- a/extensions/browser/src/browser/chrome-mcp.ts +++ b/extensions/browser/src/browser/chrome-mcp.ts @@ -258,7 +258,7 @@ function extractJsonMessage(result: ChromeMcpToolResult): unknown { } } if (lastError) { - throw lastError; + throw toLintErrorObject(lastError, "Non-Error thrown"); } return null; } @@ -629,7 +629,7 @@ async function closeChromeMcpClientAndProcess(params: { return; } await params.client.close().catch(() => {}); - await terminateChromeMcpProcessTree(rootPid, descendantPids).catch((err) => { + await terminateChromeMcpProcessTree(rootPid, descendantPids).catch((err: unknown) => { log.trace( `Unable to fully terminate Chrome MCP subprocess tree for pid ${rootPid}: ${err instanceof Error ? err.message : String(err)}`, ); @@ -761,7 +761,8 @@ async function waitForChromeMcpReady( if (signal) { racers.push( new Promise((_, reject) => { - abortListener = () => reject(signal.reason ?? new Error("aborted")); + abortListener = () => + reject(toLintErrorObject(signal.reason ?? new Error("aborted"), "Non-Error rejection")); signal.addEventListener("abort", abortListener, { once: true }); }), ); @@ -793,7 +794,8 @@ async function waitForChromeMcpPendingSession( return await Promise.race([ pending, new Promise((_, reject) => { - abortListener = () => reject(signal.reason ?? new Error("aborted")); + abortListener = () => + reject(toLintErrorObject(signal.reason ?? new Error("aborted"), "Non-Error rejection")); signal.addEventListener("abort", abortListener, { once: true }); }), ]); @@ -1022,7 +1024,8 @@ async function callTool( if (signal) { racers.push( new Promise((_, reject) => { - abortListener = () => reject(signal.reason ?? new Error("aborted")); + abortListener = () => + reject(toLintErrorObject(signal.reason ?? new Error("aborted"), "Non-Error rejection")); signal.addEventListener("abort", abortListener, { once: true }); }), ); @@ -1540,3 +1543,17 @@ export async function resetChromeMcpSessionsForTest(): Promise { await stopAllChromeMcpSessions(); chromeMcpProcessCleanupDepsForTest = null; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/client-fetch.ts b/extensions/browser/src/browser/client-fetch.ts index 56e0b36c6e9..9cfd803b102 100644 --- a/extensions/browser/src/browser/client-fetch.ts +++ b/extensions/browser/src/browser/client-fetch.ts @@ -315,9 +315,17 @@ export async function fetchBrowserJson( let abortListener: (() => void) | undefined; const abortPromise: Promise = abortCtrl.signal.aborted - ? Promise.reject(abortCtrl.signal.reason ?? new Error("aborted")) + ? Promise.reject( + toLintErrorObject(abortCtrl.signal.reason ?? new Error("aborted"), "Non-Error rejection"), + ) : new Promise((_, reject) => { - abortListener = () => reject(abortCtrl.signal.reason ?? new Error("aborted")); + abortListener = () => + reject( + toLintErrorObject( + abortCtrl.signal.reason ?? new Error("aborted"), + "Non-Error rejection", + ), + ); abortCtrl.signal.addEventListener("abort", abortListener, { once: true }); }); @@ -382,3 +390,17 @@ export const testApi = { withLoopbackBrowserAuth: withLoopbackBrowserAuthImpl, }; export { testApi as __test }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/pw-session.ts b/extensions/browser/src/browser/pw-session.ts index dfa54e1bac2..0a239032aac 100644 --- a/extensions/browser/src/browser/pw-session.ts +++ b/extensions/browser/src/browser/pw-session.ts @@ -1359,12 +1359,12 @@ export async function gotoPageWithNavigationGuard( try { const response = await opts.page.goto(opts.url, { timeout: opts.timeoutMs }); if (blockedError) { - throw blockedError; + throw toLintErrorObject(blockedError, "Non-Error thrown"); } return response; } catch (err) { if (blockedError) { - throw blockedError; + throw toLintErrorObject(blockedError, "Non-Error thrown"); } throw err; } finally { @@ -1813,3 +1813,17 @@ export async function focusPageByTargetIdViaPlaywright(opts: { } } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/pw-tools-core.interactions.ts b/extensions/browser/src/browser/pw-tools-core.interactions.ts index 12c7baab57d..ae6b4541ab9 100644 --- a/extensions/browser/src/browser/pw-tools-core.interactions.ts +++ b/extensions/browser/src/browser/pw-tools-core.interactions.ts @@ -192,7 +192,7 @@ async function assertObservedDelayedNavigations(opts: { }); } if (subframeError) { - throw subframeError; + throw toLintErrorObject(subframeError, "Non-Error thrown"); } } @@ -276,7 +276,7 @@ function scheduleDelayedInteractionNavigationGuard(opts: { const settle = (err?: unknown) => { cleanup(); if (err) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); return; } resolve(); @@ -428,11 +428,11 @@ async function assertInteractionNavigationCompletedSafely(opts: { } if (subframeError) { - throw subframeError; + throw toLintErrorObject(subframeError, "Non-Error thrown"); } if (actionError) { - throw actionError; + throw toLintErrorObject(actionError, "Non-Error thrown"); } return result as T; } @@ -478,12 +478,14 @@ function createAbortPromiseWithListener( const abortPromise: Promise = signal.aborted ? (() => { onAbort?.(signal.reason); - return Promise.reject(signal.reason ?? new Error("aborted")); + return Promise.reject( + toLintErrorObject(signal.reason ?? new Error("aborted"), "Non-Error rejection"), + ); })() : new Promise((_, reject) => { abortListener = () => { onAbort?.(signal.reason); - reject(signal.reason ?? new Error("aborted")); + reject(toLintErrorObject(signal.reason ?? new Error("aborted"), "Non-Error rejection")); }; signal.addEventListener("abort", abortListener, { once: true }); }); @@ -1712,3 +1714,17 @@ export async function batchViaPlaywright(opts: { } return { results }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/routes/agent.act.ts b/extensions/browser/src/browser/routes/agent.act.ts index 819f0402134..b70f3eb585d 100644 --- a/extensions/browser/src/browser/routes/agent.act.ts +++ b/extensions/browser/src/browser/routes/agent.act.ts @@ -178,7 +178,7 @@ async function runExistingSessionActionWithNavigationGuard(params: { } if (actionError) { - throw actionError; + throw toLintErrorObject(actionError, "Non-Error thrown"); } return result as T; @@ -809,3 +809,17 @@ export function registerBrowserAgentActRoutes( }), ); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/routes/agent.snapshot.ts b/extensions/browser/src/browser/routes/agent.snapshot.ts index eaaf6864cd7..a5e916b5cc2 100644 --- a/extensions/browser/src/browser/routes/agent.snapshot.ts +++ b/extensions/browser/src/browser/routes/agent.snapshot.ts @@ -701,7 +701,7 @@ export function registerBrowserAgentSnapshotRoutes( const pw = await getPwAiModule(); const snap = plan.wantsRoleSnapshot ? pw - ? await pw.snapshotRoleViaPlaywright(roleSnapshotArgs).catch(async (err) => { + ? await pw.snapshotRoleViaPlaywright(roleSnapshotArgs).catch(async (err: unknown) => { const fallback = await cdpRoleSnapshot(); if (fallback) { return fallback; diff --git a/extensions/browser/src/browser/routes/dispatcher.abort.test.ts b/extensions/browser/src/browser/routes/dispatcher.abort.test.ts index 87e1e45d214..b2cdc7caf4f 100644 --- a/extensions/browser/src/browser/routes/dispatcher.abort.test.ts +++ b/extensions/browser/src/browser/routes/dispatcher.abort.test.ts @@ -20,10 +20,21 @@ describe("browser route dispatcher (abort)", () => { const signal = req.signal; await new Promise((resolve, reject) => { if (signal?.aborted) { - reject(signal.reason ?? new Error("aborted")); + reject( + toLintErrorObject( + signal.reason ?? new Error("aborted"), + "Non-Error rejection", + ), + ); return; } - const onAbort = () => reject(signal?.reason ?? new Error("aborted")); + const onAbort = () => + reject( + toLintErrorObject( + signal?.reason ?? new Error("aborted"), + "Non-Error rejection", + ), + ); signal?.addEventListener("abort", onAbort, { once: true }); queueMicrotask(() => { signal?.removeEventListener("abort", onAbort); @@ -81,3 +92,17 @@ describe("browser route dispatcher (abort)", () => { expect(body.error).toBe("invalid path parameter encoding: id"); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/screenshot.ts b/extensions/browser/src/browser/screenshot.ts index dbccbd1cfe0..4ef1780539d 100644 --- a/extensions/browser/src/browser/screenshot.ts +++ b/extensions/browser/src/browser/screenshot.ts @@ -66,7 +66,7 @@ export async function normalizeBrowserScreenshot( } if (processorUnavailableError) { - throw processorUnavailableError; + throw toLintErrorObject(processorUnavailableError, "Non-Error thrown"); } const best = smallest?.buffer ?? buffer; @@ -74,3 +74,17 @@ export async function normalizeBrowserScreenshot( `Browser screenshot could not be reduced below ${(maxBytes / (1024 * 1024)).toFixed(0)}MB (got ${(best.byteLength / (1024 * 1024)).toFixed(2)}MB)`, ); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/browser/server-context.tab-ops.ts b/extensions/browser/src/browser/server-context.tab-ops.ts index 096d8e9549a..dc9d77b5d40 100644 --- a/extensions/browser/src/browser/server-context.tab-ops.ts +++ b/extensions/browser/src/browser/server-context.tab-ops.ts @@ -377,7 +377,7 @@ export function createProfileTabOps({ method: "PUT", }, getCdpControlPolicy(), - ).catch(async (err) => { + ).catch(async (err: unknown) => { if (String(err).includes("HTTP 405")) { return await fetchJson( endpoint, diff --git a/extensions/browser/src/node-host/invoke-browser.test.ts b/extensions/browser/src/node-host/invoke-browser.test.ts index 90c274e22af..b924ceaf0e1 100644 --- a/extensions/browser/src/node-host/invoke-browser.test.ts +++ b/extensions/browser/src/node-host/invoke-browser.test.ts @@ -57,7 +57,10 @@ vi.mock("../sdk-node-runtime.js", () => ({ new Promise((_, reject) => { abortCtrl.signal.addEventListener( "abort", - () => reject(abortCtrl.signal.reason ?? timeoutError), + () => + reject( + toLintErrorObject(abortCtrl.signal.reason ?? timeoutError, "Non-Error rejection"), + ), { once: true }, ); }), @@ -490,3 +493,17 @@ describe("runBrowserProxyCommand", () => { expect(dispatcherMocks.dispatch).not.toHaveBeenCalled(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/sdk-node-runtime.ts b/extensions/browser/src/sdk-node-runtime.ts index 3048e0327ec..c0ba8a3d602 100644 --- a/extensions/browser/src/sdk-node-runtime.ts +++ b/extensions/browser/src/sdk-node-runtime.ts @@ -45,11 +45,14 @@ function waitForAbort( cleanup: () => void; } { if (signal.aborted) { - return { promise: Promise.reject(signal.reason ?? fallback), cleanup: () => undefined }; + return { + promise: Promise.reject(toLintErrorObject(signal.reason ?? fallback, "Non-Error rejection")), + cleanup: () => undefined, + }; } let listener: (() => void) | undefined; const promise = new Promise((_, reject) => { - listener = () => reject(signal.reason ?? fallback); + listener = () => reject(toLintErrorObject(signal.reason ?? fallback, "Non-Error rejection")); signal.addEventListener("abort", listener, { once: true }); }); return { @@ -82,3 +85,17 @@ export async function withTimeout( abort.cleanup(); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/browser/src/server.ts b/extensions/browser/src/server.ts index 6bc02d2abd6..4f90f12814d 100644 --- a/extensions/browser/src/server.ts +++ b/extensions/browser/src/server.ts @@ -84,7 +84,7 @@ export async function startBrowserControlServerFromConfig(): Promise((resolve, reject) => { const s = app.listen(port, "127.0.0.1", () => resolve(s)); s.once("error", reject); - }).catch((err) => { + }).catch((err: unknown) => { logServer.error(`openclaw browser server failed to bind 127.0.0.1:${port}: ${String(err)}`); return null; }); diff --git a/extensions/byteplus/video-generation-provider.ts b/extensions/byteplus/video-generation-provider.ts index 89ad708fb23..d28e9ac6963 100644 --- a/extensions/byteplus/video-generation-provider.ts +++ b/extensions/byteplus/video-generation-provider.ts @@ -193,8 +193,6 @@ async function pollBytePlusTask(params: { throw new Error( readBytePlusErrorMessage(payload.error) || "BytePlus video generation failed", ); - case "queued": - case "running": default: await waitProviderOperationPollInterval({ deadline, pollIntervalMs: POLL_INTERVAL_MS }); break; diff --git a/extensions/canvas/scripts/bundle-a2ui.mjs b/extensions/canvas/scripts/bundle-a2ui.mjs index 3a498222fb0..dba65559536 100644 --- a/extensions/canvas/scripts/bundle-a2ui.mjs +++ b/extensions/canvas/scripts/bundle-a2ui.mjs @@ -222,7 +222,9 @@ async function main() { } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - await main().catch((error) => { - fail(error instanceof Error ? error.message : String(error)); - }); + await main().catch( + /** @param {unknown} error */ (error) => { + fail(error instanceof Error ? error.message : String(error)); + }, + ); } diff --git a/extensions/canvas/scripts/copy-a2ui.mjs b/extensions/canvas/scripts/copy-a2ui.mjs index 441953fd844..0c4576191a8 100644 --- a/extensions/canvas/scripts/copy-a2ui.mjs +++ b/extensions/canvas/scripts/copy-a2ui.mjs @@ -42,8 +42,10 @@ async function main() { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - main().catch((err) => { - console.error(String(err)); - process.exit(1); - }); + main().catch( + /** @param {unknown} err */ (err) => { + console.error(String(err)); + process.exit(1); + }, + ); } diff --git a/extensions/canvas/src/host/server.ts b/extensions/canvas/src/host/server.ts index adaf397351a..e839e47126c 100644 --- a/extensions/canvas/src/host/server.ts +++ b/extensions/canvas/src/host/server.ts @@ -487,7 +487,7 @@ export async function startCanvasHost(opts: CanvasHostServerOpts): Promise { + })().catch((err: unknown) => { opts.runtime.error(`Canvas host request failed: ${String(err)}`); res.statusCode = 500; res.setHeader("Content-Type", "text/plain; charset=utf-8"); diff --git a/extensions/codex-supervisor/src/mcp-serve.ts b/extensions/codex-supervisor/src/mcp-serve.ts index 9d5485b507f..bb9a233d1f0 100644 --- a/extensions/codex-supervisor/src/mcp-serve.ts +++ b/extensions/codex-supervisor/src/mcp-serve.ts @@ -11,7 +11,7 @@ function formatErrorMessage(error: unknown): string { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - serveCodexSupervisorMcp().catch((err) => { + serveCodexSupervisorMcp().catch((err: unknown) => { process.stderr.write(`codex-supervisor-serve: ${formatErrorMessage(err)}\n`); process.exit(1); }); diff --git a/extensions/codex/src/app-server/native-subagent-monitor.ts b/extensions/codex/src/app-server/native-subagent-monitor.ts index a2414285a26..5cb253eabae 100644 --- a/extensions/codex/src/app-server/native-subagent-monitor.ts +++ b/extensions/codex/src/app-server/native-subagent-monitor.ts @@ -556,7 +556,7 @@ export class CodexNativeSubagentMonitor { childState.transcriptPollTimer = setTimeout(() => { childState.transcriptPollTimer = undefined; void this.reconcileChildTranscript(childState.childThreadId) - .catch((error) => { + .catch((error: unknown) => { embeddedAgentLog.warn("Failed to reconcile Codex native subagent transcript", { childThreadId: childState.childThreadId, error: formatErrorMessage(error), @@ -595,7 +595,7 @@ export class CodexNativeSubagentMonitor { } this.taskRowReconcileTimer = setInterval( () => { - void this.reconcileKnownTaskRows().catch((error) => { + void this.reconcileKnownTaskRows().catch((error: unknown) => { embeddedAgentLog.warn("Failed to reconcile Codex native subagent task rows", { error: formatErrorMessage(error), }); diff --git a/extensions/codex/src/app-server/plugin-approval-roundtrip.ts b/extensions/codex/src/app-server/plugin-approval-roundtrip.ts index f73f4298ed0..b18b9290aca 100644 --- a/extensions/codex/src/app-server/plugin-approval-roundtrip.ts +++ b/extensions/codex/src/app-server/plugin-approval-roundtrip.ts @@ -88,10 +88,10 @@ export async function waitForPluginApprovalDecision(params: { let onAbort: (() => void) | undefined; const abortPromise = new Promise((_, reject) => { if (params.signal!.aborted) { - reject(params.signal!.reason); + reject(toLintErrorObject(params.signal!.reason, "Non-Error rejection")); return; } - onAbort = () => reject(params.signal!.reason); + onAbort = () => reject(toLintErrorObject(params.signal!.reason, "Non-Error rejection")); params.signal!.addEventListener("abort", onAbort, { once: true }); }); try { @@ -121,3 +121,17 @@ export function mapExecDecisionToOutcome( function truncateForGateway(value: string, maxLength: number): string { return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts index 6975ee3b6f2..831ca85d1a2 100644 --- a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts +++ b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts @@ -1037,7 +1037,7 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { await vi.waitFor( () => { if (runError) { - throw runError; + throw toLintErrorObject(runError, "Non-Error thrown"); } expect(harness.requests.map((request) => request.method)).toContain("turn/start"); }, @@ -1709,3 +1709,17 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { expect(maintain).not.toHaveBeenCalled(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index 97e9c81ba9e..c0bfc542224 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -696,7 +696,7 @@ describe("runCodexAppServerAttempt", () => { }); await expect( - client.request("turn/start", turnParams).catch(async (error) => { + client.request("turn/start", turnParams).catch(async (error: unknown) => { await releaseCodexSandboxExecServerEnvironment(sandbox); throw error; }), @@ -763,7 +763,7 @@ describe("runCodexAppServerAttempt", () => { nativeCodeModeOnlyEnabled: false, userMcpServersEnabled: false, environmentSelection, - }).catch(async (error) => { + }).catch(async (error: unknown) => { await releaseCodexSandboxExecServerEnvironment(sandbox); throw error; }), @@ -1237,7 +1237,7 @@ describe("runCodexAppServerAttempt", () => { params.prompt = "already persisted prompt"; params.suppressNextUserMessagePersistence = true; const readTranscript = async () => - fs.readFile(sessionFile, "utf8").catch((error) => { + fs.readFile(sessionFile, "utf8").catch((error: unknown) => { if ((error as NodeJS.ErrnoException).code === "ENOENT") { return ""; } diff --git a/extensions/codex/src/app-server/user-input-bridge.ts b/extensions/codex/src/app-server/user-input-bridge.ts index 880308ab6ba..5349905db14 100644 --- a/extensions/codex/src/app-server/user-input-bridge.ts +++ b/extensions/codex/src/app-server/user-input-bridge.ts @@ -94,9 +94,11 @@ export function createCodexUserInputBridge(params: { resolvePending(emptyUserInputResponse()); return; } - void deliverUserInputPrompt(params.paramsForRun, requestParams.questions).catch((error) => { - embeddedAgentLog.warn("failed to deliver codex user input prompt", { error }); - }); + void deliverUserInputPrompt(params.paramsForRun, requestParams.questions).catch( + (error: unknown) => { + embeddedAgentLog.warn("failed to deliver codex user input prompt", { error }); + }, + ); }); }, handleQueuedMessage(text) { diff --git a/extensions/codex/src/migration/source.ts b/extensions/codex/src/migration/source.ts index 25dca10f995..37ea7951fae 100644 --- a/extensions/codex/src/migration/source.ts +++ b/extensions/codex/src/migration/source.ts @@ -388,22 +388,24 @@ async function withPluginMigrationEligibility(params: { return evaluated; } - const snapshot = await refreshSourceAppInventory(params.requestOptions).catch((error) => { - const message = error instanceof Error ? error.message : String(error); - for (const { plugin, apps } of pending) { - evaluated.push({ - ...plugin, - migratable: false, - migrationBlock: { - code: "app_inventory_unavailable", - apps, - error: message, - }, - message: `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but source app inventory could not be read: ${message}`, - }); - } - return undefined; - }); + const snapshot = await refreshSourceAppInventory(params.requestOptions).catch( + (error: unknown) => { + const message = error instanceof Error ? error.message : String(error); + for (const { plugin, apps } of pending) { + evaluated.push({ + ...plugin, + migratable: false, + migrationBlock: { + code: "app_inventory_unavailable", + apps, + error: message, + }, + message: `Codex plugin "${plugin.pluginName ?? plugin.name}" owns apps, but source app inventory could not be read: ${message}`, + }); + } + return undefined; + }, + ); if (!snapshot) { return evaluated; } diff --git a/extensions/copilot/src/attempt.test.ts b/extensions/copilot/src/attempt.test.ts index 5b7dac9e2ba..5d6af41d20a 100644 --- a/extensions/copilot/src/attempt.test.ts +++ b/extensions/copilot/src/attempt.test.ts @@ -1396,7 +1396,7 @@ describe("runCopilotAttempt", () => { const sdk = makeFakeSdk(); const pool = makeFakePool(sdk); pool.release = vi.fn(async () => { - throw "release failed"; + throw toLintErrorObject("release failed", "Non-Error thrown"); }); await expect(runCopilotAttempt(makeParams(), { pool })).rejects.toThrow("release failed"); @@ -1414,7 +1414,7 @@ describe("runCopilotAttempt", () => { }); const pool = makeFakePool(sdk); pool.release = vi.fn(async () => { - throw "release failed"; + throw toLintErrorObject("release failed", "Non-Error thrown"); }); const result = await runCopilotAttempt(makeParams(), { pool }); @@ -2534,3 +2534,17 @@ describe("runCopilotAttempt", () => { }); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/copilot/src/event-bridge.ts b/extensions/copilot/src/event-bridge.ts index b9406e38873..8b65e7d9765 100644 --- a/extensions/copilot/src/event-bridge.ts +++ b/extensions/copilot/src/event-bridge.ts @@ -115,7 +115,7 @@ export function attachEventBridge( }); deltaChain = deltaQueue.then(() => { if (firstDeltaError !== undefined) { - throw firstDeltaError; + throw toLintErrorObject(firstDeltaError, "Non-Error thrown"); } }); void deltaChain.catch(() => undefined); @@ -354,3 +354,17 @@ function registerListener( session.off?.(eventType, handler as (...args: unknown[]) => void); }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/copilot/src/runtime.test.ts b/extensions/copilot/src/runtime.test.ts index 6378cd265eb..ff73ed0194b 100644 --- a/extensions/copilot/src/runtime.test.ts +++ b/extensions/copilot/src/runtime.test.ts @@ -419,7 +419,7 @@ describe("createCopilotClientPool", () => { it("normalizes non-Error stop failures during dispose", async () => { const sdk = makeFake({ stop: () => { - throw "stop-string"; + throw toLintErrorObject("stop-string", "Non-Error thrown"); }, }); const pool = createCopilotClientPool({ sdkFactory: sdk.fake }); @@ -485,3 +485,17 @@ describe("createCopilotClientPool", () => { expect(String(sdk.ctorCalls[0]?.baseDirectory)).toBe(normalizedHome); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/copilot/src/sdk-loader.ts b/extensions/copilot/src/sdk-loader.ts index 38aed3c4d83..eaba46af154 100755 --- a/extensions/copilot/src/sdk-loader.ts +++ b/extensions/copilot/src/sdk-loader.ts @@ -30,7 +30,7 @@ export async function loadCopilotSdk(options: LoadCopilotSdkOptions = {}): Promi const promise = doLoad(options); if (useCache) { - cached = promise.catch((err) => { + cached = promise.catch((err: unknown) => { cached = undefined; throw err; }); diff --git a/extensions/copilot/src/telemetry-bridge.test.ts b/extensions/copilot/src/telemetry-bridge.test.ts index d3999eb2c68..4fdec2b25f3 100755 --- a/extensions/copilot/src/telemetry-bridge.test.ts +++ b/extensions/copilot/src/telemetry-bridge.test.ts @@ -204,7 +204,7 @@ describe("createTraceContextProvider", () => { const onError = vi.fn(); const provider = createTraceContextProvider({ getTraceparent: () => { - throw "string-boom"; + throw toLintErrorObject("string-boom", "Non-Error thrown"); }, onError, }); @@ -236,3 +236,17 @@ describe("createTraceContextProvider", () => { expect(getTraceparent).not.toHaveBeenCalled(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/device-pair/notify.ts b/extensions/device-pair/notify.ts index 6d5d0be2926..83d6be3c39c 100644 --- a/extensions/device-pair/notify.ts +++ b/extensions/device-pair/notify.ts @@ -504,11 +504,11 @@ export function createPairingNotifierService(api: OpenClawPluginApi): OpenClawPl await notifyPendingPairingRequests({ api, statePath }); }; - await tick().catch((err) => { + await tick().catch((err: unknown) => { api.logger.warn(`device-pair: initial notify poll failed: ${formatErrorMessage(err)}`); }); notifyInterval = setInterval(() => { - tick().catch((err) => { + tick().catch((err: unknown) => { api.logger.warn(`device-pair: notify poll failed: ${formatErrorMessage(err)}`); }); }, NOTIFY_POLL_INTERVAL_MS); diff --git a/extensions/diffs/src/browser.ts b/extensions/diffs/src/browser.ts index 90b284e6879..e1e0b557baf 100644 --- a/extensions/diffs/src/browser.ts +++ b/extensions/diffs/src/browser.ts @@ -331,7 +331,7 @@ async function resolveBrowserExecutablePath(config: OpenClawConfig): Promise { + const valuePromise = resolveBrowserExecutablePathUncached(config).catch((error: unknown) => { if (executablePathCache?.valuePromise === valuePromise) { executablePathCache = null; } @@ -405,7 +405,7 @@ async function acquireSharedBrowser(params: { } return browser; }) - .catch((error) => { + .catch((error: unknown) => { if (sharedBrowserState?.browserPromise === browserPromise) { sharedBrowserState = null; } diff --git a/extensions/diffs/src/store.ts b/extensions/diffs/src/store.ts index 51c8c5a3883..7f02128b62a 100644 --- a/extensions/diffs/src/store.ts +++ b/extensions/diffs/src/store.ts @@ -226,7 +226,7 @@ export class DiffArtifactStore { this.nextCleanupAt = now + this.cleanupIntervalMs; const cleanupPromise = this.cleanupExpired() - .catch((error) => { + .catch((error: unknown) => { this.nextCleanupAt = 0; this.logger?.warn(`Failed to clean expired diff artifacts: ${String(error)}`); }) diff --git a/extensions/discord/src/client.proxy.test.ts b/extensions/discord/src/client.proxy.test.ts index 2f733e18e00..9d1ed946ddc 100644 --- a/extensions/discord/src/client.proxy.test.ts +++ b/extensions/discord/src/client.proxy.test.ts @@ -163,7 +163,7 @@ describe("createDiscordRestClient proxy support", () => { }, }) .catch((err: unknown) => { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); server.close(); }); }); @@ -175,3 +175,17 @@ describe("createDiscordRestClient proxy support", () => { expect(received.body).toContain('"attachments":[{"id":0,"filename":"image.png"}]'); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/discord/src/components.parse.ts b/extensions/discord/src/components.parse.ts index 01dcd3398c4..a5b35ab4ac4 100644 --- a/extensions/discord/src/components.parse.ts +++ b/extensions/discord/src/components.parse.ts @@ -153,7 +153,6 @@ export function mapButtonStyle(style?: DiscordComponentButtonStyle): ButtonStyle return ButtonStyle.Danger; case "link": return ButtonStyle.Link; - case "primary": default: return ButtonStyle.Primary; } diff --git a/extensions/discord/src/monitor.gateway.ts b/extensions/discord/src/monitor.gateway.ts index 918af96032b..509c2a4efd2 100644 --- a/extensions/discord/src/monitor.gateway.ts +++ b/extensions/discord/src/monitor.gateway.ts @@ -48,7 +48,7 @@ export async function waitForDiscordGatewayStop( gateway?.disconnect?.(); } finally { cleanup(); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } }; const onAbort = () => { @@ -73,3 +73,17 @@ export async function waitForDiscordGatewayStop( params.registerForceStop?.(onForceStop); }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/discord/src/monitor/ack-reactions.ts b/extensions/discord/src/monitor/ack-reactions.ts index 093a7aa13e6..4f43aa3a8c5 100644 --- a/extensions/discord/src/monitor/ack-reactions.ts +++ b/extensions/discord/src/monitor/ack-reactions.ts @@ -59,7 +59,7 @@ export function queueInitialDiscordAckReaction(params: { if (!params.shouldSendAckReaction || !params.ackReaction) { return; } - void params.reactionAdapter.setReaction(params.ackReaction).catch((err) => { + void params.reactionAdapter.setReaction(params.ackReaction).catch((err: unknown) => { logAckFailure({ log: logVerbose, channel: "discord", diff --git a/extensions/discord/src/monitor/gateway-plugin.ts b/extensions/discord/src/monitor/gateway-plugin.ts index b16c7b75c4a..60a7fe06a62 100644 --- a/extensions/discord/src/monitor/gateway-plugin.ts +++ b/extensions/discord/src/monitor/gateway-plugin.ts @@ -239,7 +239,9 @@ function createGatewayPlugin(params: { info, usedFallback: false, })) - .catch((error) => resolveGatewayInfoWithFallback({ runtime: params.runtime, error })); + .catch((error: unknown) => + resolveGatewayInfoWithFallback({ runtime: params.runtime, error }), + ); this.gatewayInfo = resolved.info; this.gatewayInfoUsedFallback = resolved.usedFallback; } diff --git a/extensions/discord/src/monitor/listeners.ts b/extensions/discord/src/monitor/listeners.ts index 7fa32947a73..387a08ce7b6 100644 --- a/extensions/discord/src/monitor/listeners.ts +++ b/extensions/discord/src/monitor/listeners.ts @@ -47,7 +47,7 @@ export class DiscordMessageListener extends MessageCreateListener { // Per-session ordering is owned by the message run queue. void Promise.resolve() .then(() => this.handler(data, client)) - .catch((err) => { + .catch((err: unknown) => { const logger = this.logger ?? discordEventQueueLog; logger.error(danger(`discord handler failed: ${String(err)}`)); }); @@ -68,7 +68,7 @@ export class DiscordInteractionListener extends InteractionCreateListener { // or compaction without blocking later gateway events. void Promise.resolve() .then(() => client.handleInteraction(data as Parameters[0], {})) - .catch((err) => { + .catch((err: unknown) => { const logger = this.logger ?? discordEventQueueLog; logger.error(danger(`discord interaction handler failed: ${String(err)}`)); }); diff --git a/extensions/discord/src/monitor/message-handler.ts b/extensions/discord/src/monitor/message-handler.ts index be0cce9e3d3..3ec38b8a6b9 100644 --- a/extensions/discord/src/monitor/message-handler.ts +++ b/extensions/discord/src/monitor/message-handler.ts @@ -113,7 +113,7 @@ function startAcceptedTypingFeedback(params: { }; activeFeedback.set(dedupeKey, { channelId, feedback: replyTypingFeedback }); ctx.replyTypingFeedback = replyTypingFeedback; - void replyTypingFeedback.onReplyStart().catch((err) => { + void replyTypingFeedback.onReplyStart().catch((err: unknown) => { logVerbose(`discord accepted typing feedback failed: ${String(err)}`); }); return replyTypingFeedback; diff --git a/extensions/discord/src/monitor/message-media.ts b/extensions/discord/src/monitor/message-media.ts index 309c199c76e..1e2df6f6d0f 100644 --- a/extensions/discord/src/monitor/message-media.ts +++ b/extensions/discord/src/monitor/message-media.ts @@ -267,7 +267,7 @@ async function fetchDiscordMedia(params: { fallbackContentType: params.fallbackContentType, originalFilename: params.originalFilename, ...(signal ? { requestInit: { signal } } : {}), - }).catch((error) => { + }).catch((error: unknown) => { if (timedOut) { return new Promise(() => {}); } @@ -365,8 +365,6 @@ function resolveStickerAssetCandidates(sticker: APIStickerItem): DiscordStickerA fileName: `${baseName}.json`, }, ]; - case StickerFormatType.APNG: - case StickerFormatType.PNG: default: return [ { url: `${DISCORD_STICKER_ASSET_BASE_URL}/${sticker.id}.png`, fileName: `${baseName}.png` }, diff --git a/extensions/discord/src/monitor/provider.lifecycle.test.ts b/extensions/discord/src/monitor/provider.lifecycle.test.ts index e6cda6c96a3..a9adddb5c9a 100644 --- a/extensions/discord/src/monitor/provider.lifecycle.test.ts +++ b/extensions/discord/src/monitor/provider.lifecycle.test.ts @@ -727,7 +727,9 @@ describe("runDiscordGatewayLifecycle", () => { waitForDiscordGatewayStopMock.mockImplementationOnce( (params: WaitForDiscordGatewayStopParams) => new Promise((_resolve, reject) => { - params.registerForceStop?.((err) => reject(err)); + params.registerForceStop?.((err) => + reject(toLintErrorObject(err, "Non-Error rejection")), + ); gateway.isConnected = false; emitter.emit("debug", "Gateway websocket opened"); }), @@ -755,3 +757,17 @@ describe("runDiscordGatewayLifecycle", () => { } }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/discord/src/voice/audio.ts b/extensions/discord/src/voice/audio.ts index 49e3c3237c1..37bc9a17cdc 100644 --- a/extensions/discord/src/voice/audio.ts +++ b/extensions/discord/src/voice/audio.ts @@ -365,7 +365,7 @@ export async function writeVoiceWavFile( function scheduleTempCleanup(tempDir: string, delayMs: number = 30 * 60 * 1000): void { const timer = setTimeout(() => { - fs.rm(tempDir, { recursive: true, force: true }).catch((err) => { + fs.rm(tempDir, { recursive: true, force: true }).catch((err: unknown) => { if (shouldLogVerbose()) { logVerbose(`discord voice: temp cleanup failed for ${tempDir}: ${formatErrorMessage(err)}`); } diff --git a/extensions/discord/src/voice/manager.ts b/extensions/discord/src/voice/manager.ts index 9a1f2a0ae75..5d7a7e150d1 100644 --- a/extensions/discord/src/voice/manager.ts +++ b/extensions/discord/src/voice/manager.ts @@ -221,7 +221,9 @@ function isUnknownDiscordVoiceStateError(err: unknown): boolean { function startAutoJoin(manager: Pick) { void manager .autoJoin() - .catch((err) => logger.warn(`discord voice: autoJoin failed: ${formatErrorMessage(err)}`)); + .catch((err: unknown) => + logger.warn(`discord voice: autoJoin failed: ${formatErrorMessage(err)}`), + ); } function resolveDiscordVoiceAgentRoute(params: { @@ -782,7 +784,7 @@ export class DiscordVoiceManager { } const speakingHandler: ((userId: string) => void) | undefined = (userId: string) => { - void this.handleSpeakingStart(entry, userId).catch((err) => { + void this.handleSpeakingStart(entry, userId).catch((err: unknown) => { logger.warn(`discord voice: capture failed: ${formatErrorMessage(err)}`); }); }; @@ -1134,7 +1136,7 @@ export class DiscordVoiceManager { return; } this.followUsersReconcileTimer = setInterval(() => { - void this.reconcileFollowedUsers("interval").catch((err) => { + void this.reconcileFollowedUsers("interval").catch((err: unknown) => { logger.warn(`discord voice: follow user reconciliation failed: ${formatErrorMessage(err)}`); }); }, FOLLOW_USERS_RECONCILE_INTERVAL_MS); @@ -1176,7 +1178,7 @@ export class DiscordVoiceManager { this.params.client.rest, plan.guildId, userId, - ).catch((err) => { + ).catch((err: unknown) => { if (!isUnknownDiscordVoiceStateError(err)) { logger.warn( `discord voice: follow user reconcile skipped transient voice state error guild=${plan.guildId} user=${userId} reason=${reason}: ${formatErrorMessage(err)}`, @@ -1415,7 +1417,7 @@ export class DiscordVoiceManager { this.params.client.rest, guildId, this.botUserId, - ).catch((err) => { + ).catch((err: unknown) => { if (!isUnknownDiscordVoiceStateError(err)) { logger.warn( `discord voice: follow reconcile skipped transient bot voice state error guild=${guildId} reason=${reason}: ${formatErrorMessage(err)}`, @@ -1473,13 +1475,17 @@ export class DiscordVoiceManager { private enqueueProcessing(entry: VoiceSessionEntry, task: () => Promise) { entry.processingQueue = entry.processingQueue .then(task) - .catch((err) => logger.warn(`discord voice: processing failed: ${formatErrorMessage(err)}`)); + .catch((err: unknown) => + logger.warn(`discord voice: processing failed: ${formatErrorMessage(err)}`), + ); } private enqueuePlayback(entry: VoiceSessionEntry, task: () => Promise) { entry.playbackQueue = entry.playbackQueue .then(task) - .catch((err) => logger.warn(`discord voice: playback failed: ${formatErrorMessage(err)}`)); + .catch((err: unknown) => + logger.warn(`discord voice: playback failed: ${formatErrorMessage(err)}`), + ); } private clearCaptureFinalizeTimer(entry: VoiceSessionEntry, userId: string, generation?: number) { @@ -1789,7 +1795,7 @@ export class DiscordVoiceManager { return; } void this.recoverFromDecryptFailures(entry) - .catch((recoverErr) => + .catch((recoverErr: unknown) => logger.warn(`discord voice: decrypt recovery failed: ${formatErrorMessage(recoverErr)}`), ) .finally(() => { diff --git a/extensions/document-extract/document-extractor.ts b/extensions/document-extract/document-extractor.ts index 13b3337381c..a093eea9181 100644 --- a/extensions/document-extract/document-extractor.ts +++ b/extensions/document-extract/document-extractor.ts @@ -15,7 +15,7 @@ async function loadPdfEngine(): Promise { if (!pdfEnginePromise) { pdfEnginePromise = import("clawpdf") .then(({ createEngine }) => createEngine()) - .catch((err) => { + .catch((err: unknown) => { pdfEnginePromise = null; throw new Error("Dependency clawpdf is required for PDF extraction", { cause: err, diff --git a/extensions/feishu/src/bot-content.ts b/extensions/feishu/src/bot-content.ts index 1f8ca47d58c..0e0fb906b6f 100644 --- a/extensions/feishu/src/bot-content.ts +++ b/extensions/feishu/src/bot-content.ts @@ -112,7 +112,6 @@ export function resolveFeishuGroupSession(params: { }) : buildFeishuConversationId({ chatId, scope: "group_sender", senderOpenId }); break; - case "group": default: peerId = chatId; break; diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index 9432bc78ea4..d47c6d191b0 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -918,7 +918,7 @@ export async function handleFeishuMessage(params: { replyToMessageId: replyTargetMessageId, replyInThread: isGroup ? (groupSession?.replyInThread ?? false) : false, accountId: account.accountId, - }).catch((err) => { + }).catch((err: unknown) => { log(`feishu[${account.accountId}]: failed to send ACP init error reply: ${String(err)}`); }); return; diff --git a/extensions/feishu/src/conversation-id.ts b/extensions/feishu/src/conversation-id.ts index 8ad1077310c..29217347c2b 100644 --- a/extensions/feishu/src/conversation-id.ts +++ b/extensions/feishu/src/conversation-id.ts @@ -37,7 +37,6 @@ export function buildFeishuConversationId(params: { return `${chatId}:topic:${topicId}`; } return senderOpenId ? `${chatId}:sender:${senderOpenId}` : chatId; - case "group": default: return chatId; } diff --git a/extensions/feishu/src/monitor.account.ts b/extensions/feishu/src/monitor.account.ts index 49cc788ea44..e26bcafd54a 100644 --- a/extensions/feishu/src/monitor.account.ts +++ b/extensions/feishu/src/monitor.account.ts @@ -272,7 +272,7 @@ function registerEventHandlers( const error = runtime?.error ?? console.error; const runFeishuHandler = async (params: { task: () => Promise; errorMessage: string }) => { if (fireAndForget) { - void params.task().catch((err) => { + void params.task().catch((err: unknown) => { error(`${params.errorMessage}: ${String(err)}`); }); return; @@ -417,7 +417,7 @@ function registerEventHandlers( accountId, }); if (fireAndForget) { - promise.catch((err) => { + promise.catch((err: unknown) => { error(`feishu[${accountId}]: error handling card action: ${String(err)}`); }); } else { diff --git a/extensions/feishu/src/monitor.bot-menu-handler.ts b/extensions/feishu/src/monitor.bot-menu-handler.ts index 2a79fc19510..3ce392ae15b 100644 --- a/extensions/feishu/src/monitor.bot-menu-handler.ts +++ b/extensions/feishu/src/monitor.bot-menu-handler.ts @@ -138,7 +138,7 @@ export function createFeishuBotMenuHandler(params: { } return await handleLegacyMenu(); }) - .catch(async (err) => { + .catch(async (err: unknown) => { if (isFeishuRetryableSyntheticEventError(err)) { await forgetProcessedFeishuMessage(syntheticMessageId, accountId, log); } else { @@ -150,7 +150,7 @@ export function createFeishuBotMenuHandler(params: { releaseFeishuMessageProcessing(syntheticMessageId, accountId); }); if (fireAndForget) { - promise.catch((err) => { + promise.catch((err: unknown) => { error(`feishu[${accountId}]: error handling bot menu event: ${String(err)}`); }); return; diff --git a/extensions/feishu/src/monitor.comment-notice-handler.ts b/extensions/feishu/src/monitor.comment-notice-handler.ts index bb359f81533..ed4ab6a4320 100644 --- a/extensions/feishu/src/monitor.comment-notice-handler.ts +++ b/extensions/feishu/src/monitor.comment-notice-handler.ts @@ -35,7 +35,7 @@ export function createFeishuDriveCommentNoticeHandler(params: { const getBotOpenId = params.getBotOpenId ?? ((id) => botOpenIds.get(id)); const runFeishuHandler = async (task: () => Promise) => { - const promise = task().catch((err) => { + const promise = task().catch((err: unknown) => { error(`feishu[${accountId}]: error handling drive comment notice: ${String(err)}`); }); if (!fireAndForget) { diff --git a/extensions/feishu/src/monitor.comment.ts b/extensions/feishu/src/monitor.comment.ts index 05f0ef9215b..ec8eddcd921 100644 --- a/extensions/feishu/src/monitor.comment.ts +++ b/extensions/feishu/src/monitor.comment.ts @@ -331,7 +331,7 @@ async function resolveParsedCommentContent(params: { resolvedObjToken: objToken, }; }) - .catch((error) => { + .catch((error: unknown) => { params.logger?.( `feishu[${params.accountId}]: wiki link resolution threw token=${link.wikiNodeToken} error=${formatErrorMessage(error)}`, ); @@ -485,7 +485,7 @@ async function requestFeishuOpenApi(params: { { timeoutMs: params.timeoutMs }, ) .then((resolved) => (resolved.status === "resolved" ? resolved.value : null)) - .catch((error) => { + .catch((error: unknown) => { params.logger?.(`${params.errorLabel}: ${formatErrorDetails(error)}`); return null; }); diff --git a/extensions/feishu/src/monitor.message-handler.ts b/extensions/feishu/src/monitor.message-handler.ts index 870464e241f..6bd43f0db9d 100644 --- a/extensions/feishu/src/monitor.message-handler.ts +++ b/extensions/feishu/src/monitor.message-handler.ts @@ -329,7 +329,7 @@ export function createFeishuMessageReceiveHandler({ await inboundDebouncer.enqueue(event); }; if (fireAndForget) { - void processMessage().catch((err) => { + void processMessage().catch((err: unknown) => { releaseFeishuMessageProcessing(messageDedupeKey, accountId); error(`feishu[${accountId}]: error handling message: ${String(err)}`); }); diff --git a/extensions/feishu/src/monitor.webhook-security.test.ts b/extensions/feishu/src/monitor.webhook-security.test.ts index 9a6b3c10f23..528d4f146cd 100644 --- a/extensions/feishu/src/monitor.webhook-security.test.ts +++ b/extensions/feishu/src/monitor.webhook-security.test.ts @@ -79,7 +79,7 @@ async function waitForSlowBodyTimeoutResponse( socket.setEncoding("utf8"); socket.on("error", () => {}); socket.on("data", (chunk) => { - response += chunk; + response += chunk.toString(); if (response.includes("Request body timeout")) { clearTimeout(failTimer); socket.destroy(); @@ -127,7 +127,7 @@ async function waitForOversizedBodyResponse(url: string): Promise { socket.setEncoding("utf8"); socket.on("data", (chunk) => { - response += chunk; + response += chunk.toString(); if (response.includes("Payload too large")) { finish(response); } diff --git a/extensions/feishu/src/session-conversation.ts b/extensions/feishu/src/session-conversation.ts index e56d6a24991..babf7b71bdf 100644 --- a/extensions/feishu/src/session-conversation.ts +++ b/extensions/feishu/src/session-conversation.ts @@ -18,7 +18,6 @@ function resolveFeishuParentConversationCandidates(rawId: string): string[] { case "group_topic": case "group_sender": return [parsed.chatId]; - case "group": default: return []; } diff --git a/extensions/feishu/src/streaming-card.ts b/extensions/feishu/src/streaming-card.ts index 28615b6ea4e..bb8efa6f9c1 100644 --- a/extensions/feishu/src/streaming-card.ts +++ b/extensions/feishu/src/streaming-card.ts @@ -520,7 +520,7 @@ export class FeishuStreamingSession { .then(async ({ release }) => { await release(); }) - .catch((e) => this.log?.(`Note update failed: ${String(e)}`)); + .catch((e: unknown) => this.log?.(`Note update failed: ${String(e)}`)); } async close(finalText?: string, options?: { note?: string }): Promise { @@ -584,7 +584,7 @@ export class FeishuStreamingSession { .then(async ({ release }) => { await release(); }) - .catch((e) => this.log?.(`Close failed: ${String(e)}`)); + .catch((e: unknown) => this.log?.(`Close failed: ${String(e)}`)); const finalState = this.state; this.state = null; this.pendingText = null; diff --git a/extensions/google-meet/src/agent-consult.ts b/extensions/google-meet/src/agent-consult.ts index 046616c5cde..6dac4b704f6 100644 --- a/extensions/google-meet/src/agent-consult.ts +++ b/extensions/google-meet/src/agent-consult.ts @@ -144,7 +144,7 @@ export function handleGoogleMeetRealtimeConsultToolCall(params: { }); params.session.submitToolResult(callId, result); }) - .catch((error: Error) => { + .catch((error: unknown) => { params.onTalkEvent?.({ type: "tool.error", callId, diff --git a/extensions/google-meet/src/realtime-node.ts b/extensions/google-meet/src/realtime-node.ts index 69343aee75f..7450404c16c 100644 --- a/extensions/google-meet/src/realtime-node.ts +++ b/extensions/google-meet/src/realtime-node.ts @@ -277,7 +277,7 @@ export async function startNodeAgentAudioBridge(params: { ), ); }) - .catch((error) => { + .catch((error: unknown) => { params.logger.warn(`[google-meet] node agent TTS failed: ${formatErrorMessage(error)}`); }); }; @@ -557,7 +557,7 @@ export async function startNodeRealtimeAudioBridge(params: { }, timeoutMs: 5_000, }) - .catch((error) => { + .catch((error: unknown) => { params.logger.warn( `[google-meet] node audio output failed: ${formatErrorMessage(error)}`, ); @@ -580,7 +580,7 @@ export async function startNodeRealtimeAudioBridge(params: { }, timeoutMs: 5_000, }) - .catch((error) => { + .catch((error: unknown) => { params.logger.warn( `[google-meet] node audio clear failed: ${formatErrorMessage(error)}`, ); diff --git a/extensions/google-meet/src/realtime.ts b/extensions/google-meet/src/realtime.ts index ade1b010b6d..bbb926cbdd9 100644 --- a/extensions/google-meet/src/realtime.ts +++ b/extensions/google-meet/src/realtime.ts @@ -696,7 +696,7 @@ export async function startCommandAgentAudioBridge(params: { }); endTalkTurn(); }) - .catch((error) => { + .catch((error: unknown) => { params.logger.warn(`[google-meet] agent TTS failed: ${formatErrorMessage(error)}`); }); }; diff --git a/extensions/google-meet/src/voice-call-gateway.ts b/extensions/google-meet/src/voice-call-gateway.ts index b3fd202781b..fa0efef466c 100644 --- a/extensions/google-meet/src/voice-call-gateway.ts +++ b/extensions/google-meet/src/voice-call-gateway.ts @@ -68,7 +68,7 @@ async function createConnectedGatewayClient( reject(new Error("gateway event loop readiness timeout")); } }) - .catch((err) => { + .catch((err: unknown) => { clearTimeout(timer); reject(err instanceof Error ? err : new Error(String(err))); }); diff --git a/extensions/google/google.live.test.ts b/extensions/google/google.live.test.ts index 1000d9dc206..d166e9d59d0 100644 --- a/extensions/google/google.live.test.ts +++ b/extensions/google/google.live.test.ts @@ -140,7 +140,7 @@ describeLive("google plugin live", () => { } } if (lastError) { - throw lastError; + throw toLintErrorObject(lastError, "Non-Error thrown"); } expect(result?.provider).toBe("gemini"); @@ -177,3 +177,17 @@ describeLive("google plugin live", () => { }); }, 120_000); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/google/transport-stream.test.ts b/extensions/google/transport-stream.test.ts index 05bde93ca1b..43eb2ab6b60 100644 --- a/extensions/google/transport-stream.test.ts +++ b/extensions/google/transport-stream.test.ts @@ -637,7 +637,12 @@ describe("google transport stream", () => { (_url: string, init?: RequestInit) => new Promise((_resolve, reject) => { init?.signal?.addEventListener("abort", () => { - reject(init.signal?.reason ?? new Error("aborted")); + reject( + toLintErrorObject( + init.signal?.reason ?? new Error("aborted"), + "Non-Error rejection", + ), + ); }); }), ) @@ -1984,3 +1989,17 @@ describe("google transport stream", () => { ]); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/googlechat/src/monitor-webhook.ts b/extensions/googlechat/src/monitor-webhook.ts index 392f9cacd32..f4840444581 100644 --- a/extensions/googlechat/src/monitor-webhook.ts +++ b/extensions/googlechat/src/monitor-webhook.ts @@ -287,7 +287,7 @@ export function createGoogleChatWebhookRequestHandler(params: { const dispatchTarget = selectedTarget; dispatchTarget.statusSink?.({ lastInboundAt: Date.now() }); - params.processEvent(parsedEvent, dispatchTarget).catch((err) => { + params.processEvent(parsedEvent, dispatchTarget).catch((err: unknown) => { dispatchTarget.runtime.error?.( `[${dispatchTarget.account.accountId}] Google Chat webhook failed: ${String(err)}`, ); diff --git a/extensions/imessage/src/monitor.watch-subscribe-retry.test.ts b/extensions/imessage/src/monitor.watch-subscribe-retry.test.ts index fb8207f6021..b1931b19910 100644 --- a/extensions/imessage/src/monitor.watch-subscribe-retry.test.ts +++ b/extensions/imessage/src/monitor.watch-subscribe-retry.test.ts @@ -132,7 +132,7 @@ describe("monitorIMessageProvider watch.subscribe startup retry", () => { const monitorErrorPromise = monitorIMessageProvider({ config: { channels: { imessage: {} } } as never, runtime: runtime as never, - }).catch((error) => error); + }).catch((error: unknown) => error); await vi.runAllTimersAsync(); const monitorError = await monitorErrorPromise; diff --git a/extensions/imessage/src/monitor/monitor-provider.ts b/extensions/imessage/src/monitor/monitor-provider.ts index a74006df575..fbb83092d56 100644 --- a/extensions/imessage/src/monitor/monitor-provider.ts +++ b/extensions/imessage/src/monitor/monitor-provider.ts @@ -1036,7 +1036,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P runtime, onNotification: (msg) => { if (msg.method === "message") { - void handleMessage(msg.params).catch((err) => { + void handleMessage(msg.params).catch((err: unknown) => { runtime.error?.(`imessage: handler failed: ${String(err)}`); }); } else if (msg.method === "error") { diff --git a/extensions/irc/src/client.ts b/extensions/irc/src/client.ts index e29cd2d0534..309ca9c69ce 100644 --- a/extensions/irc/src/client.ts +++ b/extensions/irc/src/client.ts @@ -376,7 +376,7 @@ export async function connectIrcClient(options: IrcClientOptions): Promise { + ).catch((error: unknown) => { fail(error); }); } diff --git a/extensions/line/src/bot-handlers.ts b/extensions/line/src/bot-handlers.ts index 14d596edbe5..ef05617a5bb 100644 --- a/extensions/line/src/bot-handlers.ts +++ b/extensions/line/src/bot-handlers.ts @@ -615,6 +615,20 @@ export async function handleLineWebhookEvents( } } if (firstError) { - throw firstError; + throw toLintErrorObject(firstError, "Non-Error thrown"); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/line/src/monitor.ts b/extensions/line/src/monitor.ts index d5722e29b57..e585c3d22b0 100644 --- a/extensions/line/src/monitor.ts +++ b/extensions/line/src/monitor.ts @@ -437,7 +437,7 @@ export async function monitorLineProvider( logVerbose(`line: received ${body.events.length} webhook events`); void Promise.resolve() .then(() => match.target.bot.handleWebhook(body)) - .catch((err) => { + .catch((err: unknown) => { match.target.runtime.error?.( danger(`line webhook dispatch failed: ${String(err)}`), ); diff --git a/extensions/line/src/send.ts b/extensions/line/src/send.ts index fde40a23e30..2342b79ea8f 100644 --- a/extensions/line/src/send.ts +++ b/extensions/line/src/send.ts @@ -224,7 +224,7 @@ async function pushLineMessages( }); if (behavior.errorContext) { - await pushRequest.catch((err) => { + await pushRequest.catch((err: unknown) => { logLineHttpError(err, behavior.errorContext!); throw err; }); @@ -301,7 +301,6 @@ export async function sendMessageLine( case "audio": messages.push(createAudioMessage(mediaUrl, opts.durationMs ?? 60000)); break; - case "image": default: // Backward compatibility: keep image as default when media kind is unspecified. { diff --git a/extensions/line/src/webhook-node.ts b/extensions/line/src/webhook-node.ts index 0cacc8c17ca..151a2cc5e2a 100644 --- a/extensions/line/src/webhook-node.ts +++ b/extensions/line/src/webhook-node.ts @@ -128,7 +128,7 @@ export function createLineNodeWebhookHandler(params: { logVerbose(`line: received ${body.events.length} webhook events`); void Promise.resolve() .then(() => params.bot.handleWebhook(body)) - .catch((err) => logLineWebhookDispatchError(params.runtime, err)); + .catch((err: unknown) => logLineWebhookDispatchError(params.runtime, err)); } } catch (err) { await receiveContext?.nack(err); diff --git a/extensions/line/src/webhook.ts b/extensions/line/src/webhook.ts index be30956b2ed..75748137111 100644 --- a/extensions/line/src/webhook.ts +++ b/extensions/line/src/webhook.ts @@ -93,7 +93,7 @@ export function createLineWebhookMiddleware( logVerbose(`line: received ${body.events.length} webhook events`); void Promise.resolve() .then(() => onEvents(body)) - .catch((err) => logLineWebhookDispatchError(runtime, err)); + .catch((err: unknown) => logLineWebhookDispatchError(runtime, err)); } } catch (err) { await receiveContext?.nack(err); diff --git a/extensions/lmstudio/src/stream.ts b/extensions/lmstudio/src/stream.ts index 409141debcd..270e4f0cf79 100644 --- a/extensions/lmstudio/src/stream.ts +++ b/extensions/lmstudio/src/stream.ts @@ -217,7 +217,7 @@ export function wrapLmstudioInferencePreload(ctx: ProviderWrapStreamFnContext): () => { recordPreloadSuccess(preloadKey); }, - (error) => { + (error: unknown) => { const entry = recordPreloadFailure(preloadKey, Date.now()); throw Object.assign(new Error("preload-failed"), { cause: error, diff --git a/extensions/lobster/src/lobster-runner.test.ts b/extensions/lobster/src/lobster-runner.test.ts index 0ad50683a8d..0c432418db0 100644 --- a/extensions/lobster/src/lobster-runner.test.ts +++ b/extensions/lobster/src/lobster-runner.test.ts @@ -583,7 +583,12 @@ describe("createEmbeddedLobsterRunner", () => { ); ctx?.signal?.addEventListener("abort", () => { clearTimeout(timeout); - reject(ctx.signal?.reason ?? new Error("aborted")); + reject( + toLintErrorObject( + ctx.signal?.reason ?? new Error("aborted"), + "Non-Error rejection", + ), + ); }); }), ), @@ -605,3 +610,17 @@ describe("createEmbeddedLobsterRunner", () => { ).rejects.toThrow(/timed out|aborted/); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/lobster/src/lobster-runner.ts b/extensions/lobster/src/lobster-runner.ts index 63981d42c06..aaf134bde82 100644 --- a/extensions/lobster/src/lobster-runner.ts +++ b/extensions/lobster/src/lobster-runner.ts @@ -286,9 +286,9 @@ async function withTimeout( clearTimeout(timer); resolve(value); }, - (error) => { + (error: unknown) => { clearTimeout(timer); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); @@ -392,3 +392,17 @@ export function createEmbeddedLobsterRunner(options?: { }, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/matrix/src/matrix/client/file-sync-store.ts b/extensions/matrix/src/matrix/client/file-sync-store.ts index 1ec5e97eca6..af951fea3e5 100644 --- a/extensions/matrix/src/matrix/client/file-sync-store.ts +++ b/extensions/matrix/src/matrix/client/file-sync-store.ts @@ -259,7 +259,7 @@ export class FileBackedMatrixSyncStore extends MemoryStore { } this.persistTimer = setTimeout(() => { this.persistTimer = null; - void this.flush().catch((err) => { + void this.flush().catch((err: unknown) => { LogService.warn("MatrixFileSyncStore", "Failed to persist Matrix sync store:", err); }); }, PERSIST_DEBOUNCE_MS); diff --git a/extensions/matrix/src/matrix/monitor/events.test.ts b/extensions/matrix/src/matrix/monitor/events.test.ts index 6840c5f4fea..81852152d48 100644 --- a/extensions/matrix/src/matrix/monitor/events.test.ts +++ b/extensions/matrix/src/matrix/monitor/events.test.ts @@ -112,7 +112,7 @@ function createHarness(params?: { const runDetachedTask = vi.fn((_label: string, task: () => Promise) => { const promise = Promise.resolve() .then(task) - .catch((error) => { + .catch((error: unknown) => { throw error; }) .finally(() => { diff --git a/extensions/matrix/src/matrix/monitor/events.ts b/extensions/matrix/src/matrix/monitor/events.ts index f8a012d9c9f..f8032d79d2e 100644 --- a/extensions/matrix/src/matrix/monitor/events.ts +++ b/extensions/matrix/src/matrix/monitor/events.ts @@ -230,7 +230,7 @@ export function registerMatrixMonitorEvents(params: { } return Promise.resolve() .then(task) - .catch((error) => { + .catch((error: unknown) => { logVerboseMessage(`matrix: ${label} failed (${String(error)})`); }); }; diff --git a/extensions/matrix/src/matrix/monitor/handler.ts b/extensions/matrix/src/matrix/monitor/handler.ts index 26992f01e35..294be5afb4c 100644 --- a/extensions/matrix/src/matrix/monitor/handler.ts +++ b/extensions/matrix/src/matrix/monitor/handler.ts @@ -905,12 +905,14 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam if (!isPollEvent) { return null; } - pollSnapshotPromise ??= fetchMatrixPollSnapshot(client, roomId, event).catch((err) => { - logVerboseMessage( - `matrix: failed resolving poll snapshot room=${roomId} id=${event.event_id ?? "unknown"}: ${String(err)}`, - ); - return null; - }); + pollSnapshotPromise ??= fetchMatrixPollSnapshot(client, roomId, event).catch( + (err: unknown) => { + logVerboseMessage( + `matrix: failed resolving poll snapshot room=${roomId} id=${event.event_id ?? "unknown"}: ${String(err)}`, + ); + return null; + }, + ); return await pollSnapshotPromise; }; @@ -1481,7 +1483,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam .then(({ reactMatrixMessage }) => reactMatrixMessage(roomId, messageId, ackReaction, client), ) - .catch((err) => { + .catch((err: unknown) => { logVerboseMessage(`matrix react failed for room ${roomId}: ${String(err)}`); }); } @@ -1489,7 +1491,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam if (messageId) { loadMatrixSendModule() .then(({ sendReadReceiptMatrix }) => sendReadReceiptMatrix(roomId, messageId, client)) - .catch((err) => { + .catch((err: unknown) => { logVerboseMessage( `matrix: read receipt failed room=${roomId} id=${messageId}: ${String(err)}`, ); diff --git a/extensions/matrix/src/matrix/monitor/inbound-dedupe.ts b/extensions/matrix/src/matrix/monitor/inbound-dedupe.ts index 1195e4eef99..97ba947c93c 100644 --- a/extensions/matrix/src/matrix/monitor/inbound-dedupe.ts +++ b/extensions/matrix/src/matrix/monitor/inbound-dedupe.ts @@ -281,7 +281,7 @@ export async function createMatrixInboundEventDeduper(params: { }, ttlMs > 0 ? { ttlMs } : undefined, ) - .catch((err) => { + .catch((err: unknown) => { LogService.warn( "MatrixInboundDedupe", "Failed persisting Matrix inbound dedupe entry:", diff --git a/extensions/matrix/src/matrix/monitor/index.ts b/extensions/matrix/src/matrix/monitor/index.ts index a8d25818222..916c4331235 100644 --- a/extensions/matrix/src/matrix/monitor/index.ts +++ b/extensions/matrix/src/matrix/monitor/index.ts @@ -494,7 +494,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi auth, env: process.env, abortSignal: opts.abortSignal, - }).catch((err) => { + }).catch((err: unknown) => { logVerboseMessage(`matrix: failed to backfill deviceId after startup (${String(err)})`); }); diff --git a/extensions/matrix/src/matrix/monitor/reaction-events.ts b/extensions/matrix/src/matrix/monitor/reaction-events.ts index 2cb69950343..c7a4be2183d 100644 --- a/extensions/matrix/src/matrix/monitor/reaction-events.ts +++ b/extensions/matrix/src/matrix/monitor/reaction-events.ts @@ -136,12 +136,14 @@ export async function handleInboundMatrixReaction(params: { return; } - const targetEvent = await params.client.getEvent(params.roomId, reaction.eventId).catch((err) => { - params.logVerboseMessage( - `matrix: failed resolving reaction target room=${params.roomId} id=${reaction.eventId}: ${String(err)}`, - ); - return null; - }); + const targetEvent = await params.client + .getEvent(params.roomId, reaction.eventId) + .catch((err: unknown) => { + params.logVerboseMessage( + `matrix: failed resolving reaction target room=${params.roomId} id=${reaction.eventId}: ${String(err)}`, + ); + return null; + }); const targetSender = targetEvent && typeof targetEvent.sender === "string" ? targetEvent.sender.trim() : ""; if (!targetSender) { diff --git a/extensions/matrix/src/matrix/monitor/reply-context.ts b/extensions/matrix/src/matrix/monitor/reply-context.ts index c506f48bbe8..cee6f227004 100644 --- a/extensions/matrix/src/matrix/monitor/reply-context.ts +++ b/extensions/matrix/src/matrix/monitor/reply-context.ts @@ -56,12 +56,14 @@ export function createMatrixReplyContextResolver(params: { return cached; } - const event = await params.client.getEvent(input.roomId, input.eventId).catch((err) => { - params.logVerboseMessage( - `matrix: failed resolving reply context room=${input.roomId} id=${input.eventId}: ${String(err)}`, - ); - return null; - }); + const event = await params.client + .getEvent(input.roomId, input.eventId) + .catch((err: unknown) => { + params.logVerboseMessage( + `matrix: failed resolving reply context room=${input.roomId} id=${input.eventId}: ${String(err)}`, + ); + return null; + }); if (!event) { // Do not cache failures so transient errors can be retried on the next // message that references the same event. diff --git a/extensions/matrix/src/matrix/monitor/task-runner.ts b/extensions/matrix/src/matrix/monitor/task-runner.ts index 31c31662838..5d1e9a082bd 100644 --- a/extensions/matrix/src/matrix/monitor/task-runner.ts +++ b/extensions/matrix/src/matrix/monitor/task-runner.ts @@ -9,7 +9,7 @@ export function createMatrixMonitorTaskRunner(params: { const runDetachedTask = (label: string, task: () => Promise): Promise => { const trackedTask: Promise = Promise.resolve() .then(task) - .catch((error) => { + .catch((error: unknown) => { const message = String(error); params.logVerboseMessage(`matrix: ${label} failed (${message})`); params.logger.warn("matrix background task failed", { diff --git a/extensions/matrix/src/matrix/monitor/thread-context.ts b/extensions/matrix/src/matrix/monitor/thread-context.ts index 9e7e321af65..24bfe5c7480 100644 --- a/extensions/matrix/src/matrix/monitor/thread-context.ts +++ b/extensions/matrix/src/matrix/monitor/thread-context.ts @@ -74,7 +74,7 @@ export function createMatrixThreadContextResolver(params: { const rootEvent = await params.client .getEvent(input.roomId, input.threadRootId) - .catch((err) => { + .catch((err: unknown) => { params.logVerboseMessage( `matrix: failed resolving thread root room=${input.roomId} id=${input.threadRootId}: ${String(err)}`, ); diff --git a/extensions/matrix/src/matrix/monitor/verification-events.ts b/extensions/matrix/src/matrix/monitor/verification-events.ts index f41b9cbaa98..e02d055d07a 100644 --- a/extensions/matrix/src/matrix/monitor/verification-events.ts +++ b/extensions/matrix/src/matrix/monitor/verification-events.ts @@ -628,7 +628,7 @@ export function createMatrixVerificationEventRouter(params: { routeTask, ); } else { - void routeTask().catch((err) => { + void routeTask().catch((err: unknown) => { params.logVerboseMessage(`matrix: failed routing verification event: ${String(err)}`); }); } diff --git a/extensions/matrix/src/matrix/sdk/idb-persistence.test-helpers.ts b/extensions/matrix/src/matrix/sdk/idb-persistence.test-helpers.ts index eaffafe4859..6a478ebcbb9 100644 --- a/extensions/matrix/src/matrix/sdk/idb-persistence.test-helpers.ts +++ b/extensions/matrix/src/matrix/sdk/idb-persistence.test-helpers.ts @@ -11,7 +11,11 @@ export async function clearAllIndexedDbState(params?: { databasePrefix?: string new Promise((resolve, reject) => { const req = indexedDB.deleteDatabase(name); req.addEventListener("success", () => resolve(), { once: true }); - req.addEventListener("error", () => reject(req.error), { once: true }); + req.addEventListener( + "error", + () => reject(toLintErrorObject(req.error, "Non-Error rejection")), + { once: true }, + ); req.addEventListener("blocked", () => resolve(), { once: true }); }), ), @@ -43,9 +47,17 @@ export async function seedDatabase(params: { db.close(); resolve(); }); - tx.addEventListener("error", () => reject(tx.error), { once: true }); + tx.addEventListener( + "error", + () => reject(toLintErrorObject(tx.error, "Non-Error rejection")), + { once: true }, + ); }); - req.addEventListener("error", () => reject(req.error), { once: true }); + req.addEventListener( + "error", + () => reject(toLintErrorObject(req.error, "Non-Error rejection")), + { once: true }, + ); }); } @@ -82,9 +94,35 @@ export async function readDatabaseRecords(params: { values = valuesReq.result; maybeResolve(); }); - keysReq.addEventListener("error", () => reject(keysReq.error), { once: true }); - valuesReq.addEventListener("error", () => reject(valuesReq.error), { once: true }); + keysReq.addEventListener( + "error", + () => reject(toLintErrorObject(keysReq.error, "Non-Error rejection")), + { once: true }, + ); + valuesReq.addEventListener( + "error", + () => reject(toLintErrorObject(valuesReq.error, "Non-Error rejection")), + { once: true }, + ); }); - req.addEventListener("error", () => reject(req.error), { once: true }); + req.addEventListener( + "error", + () => reject(toLintErrorObject(req.error, "Non-Error rejection")), + { once: true }, + ); }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/matrix/src/matrix/sdk/idb-persistence.ts b/extensions/matrix/src/matrix/sdk/idb-persistence.ts index 3d9f8943517..2da5fb03859 100644 --- a/extensions/matrix/src/matrix/sdk/idb-persistence.ts +++ b/extensions/matrix/src/matrix/sdk/idb-persistence.ts @@ -97,7 +97,11 @@ function parseSnapshotPayload(data: string): IdbDatabaseSnapshot[] | null { function idbReq(req: IDBRequest): Promise { return new Promise((resolve, reject) => { req.addEventListener("success", () => resolve(req.result), { once: true }); - req.addEventListener("error", () => reject(req.error), { once: true }); + req.addEventListener( + "error", + () => reject(toLintErrorObject(req.error, "Non-Error rejection")), + { once: true }, + ); }); } @@ -117,7 +121,9 @@ async function dumpIndexedDatabases(databasePrefix?: string): Promise { const r = idb.open(name, version); r.addEventListener("success", () => resolve(r.result), { once: true }); - r.addEventListener("error", () => reject(r.error), { once: true }); + r.addEventListener("error", () => reject(toLintErrorObject(r.error, "Non-Error rejection")), { + once: true, + }); }); const stores: IdbStoreSnapshot[] = []; @@ -203,7 +209,9 @@ async function restoreIndexedDatabases(snapshot: IdbDatabaseSnapshot[]): Promise }, { once: true }, ); - r.addEventListener("error", () => reject(r.error), { once: true }); + r.addEventListener("error", () => reject(toLintErrorObject(r.error, "Non-Error rejection")), { + once: true, + }); }); } } @@ -284,3 +292,17 @@ export async function persistIdbToDisk(params?: { LogService.warn("IdbPersistence", "Failed to persist IndexedDB snapshot:", err); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/matrix/src/matrix/sdk/verification-manager.ts b/extensions/matrix/src/matrix/sdk/verification-manager.ts index c2178e71aba..8078fab1198 100644 --- a/extensions/matrix/src/matrix/sdk/verification-manager.ts +++ b/extensions/matrix/src/matrix/sdk/verification-manager.ts @@ -394,7 +394,7 @@ export class MatrixVerificationManager { .then(() => { this.touchVerificationSession(session); }) - .catch((err) => { + .catch((err: unknown) => { session.acceptRequested = false; session.error = formatMatrixErrorMessage(err); this.touchVerificationSession(session); @@ -516,7 +516,7 @@ export class MatrixVerificationManager { .then(() => { this.touchVerificationSession(session); }) - .catch((err) => { + .catch((err: unknown) => { session.error = formatMatrixErrorMessage(err); this.touchVerificationSession(session); }); @@ -545,7 +545,7 @@ export class MatrixVerificationManager { .then(() => { this.touchVerificationSession(session); }) - .catch((err) => { + .catch((err: unknown) => { session.error = formatMatrixErrorMessage(err); this.touchVerificationSession(session); }); diff --git a/extensions/matrix/src/matrix/startup-abort.ts b/extensions/matrix/src/matrix/startup-abort.ts index 71b530627da..4936dfab042 100644 --- a/extensions/matrix/src/matrix/startup-abort.ts +++ b/extensions/matrix/src/matrix/startup-abort.ts @@ -35,10 +35,24 @@ export async function awaitMatrixStartupWithAbort( abortSignal.removeEventListener("abort", onAbort); resolve(value); }, - (error) => { + (error: unknown) => { abortSignal.removeEventListener("abort", onAbort); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/matrix/src/matrix/thread-bindings.ts b/extensions/matrix/src/matrix/thread-bindings.ts index ff4f6b9d1ae..c5123690798 100644 --- a/extensions/matrix/src/matrix/thread-bindings.ts +++ b/extensions/matrix/src/matrix/thread-bindings.ts @@ -368,7 +368,7 @@ export async function createMatrixThreadBindingManager(params: { }; const persist = async () => await enqueuePersist(); const persistSafely = (reason: string, bindings?: MatrixThreadBindingRecord[]) => { - void enqueuePersist(bindings).catch((err) => { + void enqueuePersist(bindings).catch((err: unknown) => { params.logVerboseMessage?.( `matrix: failed persisting thread bindings account=${params.accountId} action=${reason}: ${String(err)}`, ); @@ -383,7 +383,7 @@ export async function createMatrixThreadBindingManager(params: { await persist(); } await migrationStore.register(legacyImportKey, { importedAt: Date.now() }); - await fs.rm(legacyFilePath, { force: true }).catch((err) => { + await fs.rm(legacyFilePath, { force: true }).catch((err: unknown) => { params.logVerboseMessage?.( `matrix: failed removing migrated legacy thread bindings account=${params.accountId}: ${String(err)}`, ); @@ -693,7 +693,7 @@ export async function createMatrixThreadBindingManager(params: { await sendFarewellMessages(removed, (record) => reasonByBindingKey.get(resolveBindingKey(record)), ); - })().catch((err) => { + })().catch((err: unknown) => { params.logVerboseMessage?.( `matrix: failed auto-unbinding expired bindings account=${params.accountId}: ${String(err)}`, ); diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index dda2506dd8f..001da06b739 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -2165,7 +2165,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} client, commands, log: (msg) => runtime.log?.(msg), - }).catch((err) => { + }).catch((err: unknown) => { runtime.error?.(`mattermost: slash cleanup failed: ${String(err)}`); }); }; diff --git a/extensions/memory-core/src/dreaming-narrative.ts b/extensions/memory-core/src/dreaming-narrative.ts index 9eaa2b81816..25d9eba0d7f 100644 --- a/extensions/memory-core/src/dreaming-narrative.ts +++ b/extensions/memory-core/src/dreaming-narrative.ts @@ -500,8 +500,8 @@ export function formatBackfillDiaryDate(isoDay: string, _timezone?: string): str } async function assertSafeDreamsPath(dreamsPath: string): Promise { - const stat = await fs.lstat(dreamsPath).catch((err: NodeJS.ErrnoException) => { - if (err.code === "ENOENT") { + const stat = await fs.lstat(dreamsPath).catch((err: unknown) => { + if (extractErrorCode(err) === "ENOENT") { return null; } throw err; diff --git a/extensions/memory-core/src/dreaming-repair.ts b/extensions/memory-core/src/dreaming-repair.ts index c24b524e564..21169f55d27 100644 --- a/extensions/memory-core/src/dreaming-repair.ts +++ b/extensions/memory-core/src/dreaming-repair.ts @@ -1,6 +1,7 @@ import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; +import { extractErrorCode } from "openclaw/plugin-sdk/error-runtime"; type DreamingArtifactsAuditIssue = { severity: "warn" | "error"; @@ -87,8 +88,8 @@ function buildArchiveTimestamp(now: Date): string { } async function ensureArchivablePath(targetPath: string): Promise<"file" | "dir" | null> { - const stat = await fs.lstat(targetPath).catch((err: NodeJS.ErrnoException) => { - if (err.code === "ENOENT") { + const stat = await fs.lstat(targetPath).catch((err: unknown) => { + if (extractErrorCode(err) === "ENOENT") { return null; } throw err; diff --git a/extensions/memory-core/src/dreaming.ts b/extensions/memory-core/src/dreaming.ts index e29d9d72d0b..9d22af6577b 100644 --- a/extensions/memory-core/src/dreaming.ts +++ b/extensions/memory-core/src/dreaming.ts @@ -860,7 +860,7 @@ export function registerShortTermPromotionDreaming(api: OpenClawPluginApi): void } scheduleStartupCronRetry(); }) - .catch((err) => { + .catch((err: unknown) => { if (disposed) { return; } @@ -877,7 +877,7 @@ export function registerShortTermPromotionDreaming(api: OpenClawPluginApi): void return; } runtimeCronReconcileTimer = setInterval(() => { - void reconcileManagedDreamingCron({ reason: "runtime" }).catch((err) => { + void reconcileManagedDreamingCron({ reason: "runtime" }).catch((err: unknown) => { api.logger.error(`memory-core: dreaming cron reconcile failed: ${formatErrorMessage(err)}`); }); }, RUNTIME_CRON_RECONCILE_INTERVAL_MS); diff --git a/extensions/memory-core/src/memory/manager-async-state.ts b/extensions/memory-core/src/memory/manager-async-state.ts index 83613a303c1..d1556eb2022 100644 --- a/extensions/memory-core/src/memory/manager-async-state.ts +++ b/extensions/memory-core/src/memory/manager-async-state.ts @@ -8,7 +8,7 @@ export function startAsyncSearchSync(params: { if (!params.enabled || (!params.dirty && !params.sessionsDirty)) { return; } - void params.sync({ reason: "search" }).catch((err) => { + void params.sync({ reason: "search" }).catch((err: unknown) => { params.onError(err); }); } diff --git a/extensions/memory-core/src/memory/manager-embedding-timeout.test.ts b/extensions/memory-core/src/memory/manager-embedding-timeout.test.ts index 5c9ee0d3af4..13a2a8e4061 100644 --- a/extensions/memory-core/src/memory/manager-embedding-timeout.test.ts +++ b/extensions/memory-core/src/memory/manager-embedding-timeout.test.ts @@ -106,7 +106,11 @@ describe("memory embedding timeout abort", () => { run: async (signal) => { signalSeen = signal; return await new Promise((resolve, reject) => { - signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + signal.addEventListener( + "abort", + () => reject(toLintErrorObject(signal.reason, "Non-Error rejection")), + { once: true }, + ); }); }, }), @@ -198,3 +202,17 @@ describe("memory index concurrency resolution", () => { ).toBe(3); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/memory-core/src/memory/manager-sync-ops.ts b/extensions/memory-core/src/memory/manager-sync-ops.ts index 288fe9e3f31..8a4d1a2efbe 100644 --- a/extensions/memory-core/src/memory/manager-sync-ops.ts +++ b/extensions/memory-core/src/memory/manager-sync-ops.ts @@ -169,7 +169,7 @@ function shouldIgnoreMemoryWatchPath( } export function runDetachedMemorySync(sync: () => Promise, reason: "interval" | "watch") { - void sync().catch((err) => { + void sync().catch((err: unknown) => { log.warn(`memory sync failed (${reason}): ${String(err)}`); }); } @@ -792,7 +792,7 @@ export abstract class MemoryManagerSyncOps { if (!this.sources.has("sessions")) { return; } - void this.runSessionStartupCatchup().catch((err) => { + void this.runSessionStartupCatchup().catch((err: unknown) => { log.warn("memory session startup catch-up failed: " + String(err)); }); } @@ -849,7 +849,7 @@ export abstract class MemoryManagerSyncOps { if (dirtyFiles.length === 0 || this.closed) { return dirtyFiles; } - void this.sync({ reason: "session-startup-catchup" }).catch((err) => { + void this.sync({ reason: "session-startup-catchup" }).catch((err: unknown) => { log.warn("memory sync failed (session-startup-catchup): " + String(err)); }); return dirtyFiles; @@ -862,7 +862,7 @@ export abstract class MemoryManagerSyncOps { } this.sessionWatchTimer = setTimeout(() => { this.sessionWatchTimer = null; - void this.processSessionDeltaBatch().catch((err) => { + void this.processSessionDeltaBatch().catch((err: unknown) => { log.warn(`memory session delta failed: ${String(err)}`); }); }, SESSION_DIRTY_DEBOUNCE_MS); @@ -918,7 +918,7 @@ export abstract class MemoryManagerSyncOps { shouldSync = true; } if (shouldSync) { - void this.sync({ reason: "session-delta" }).catch((err) => { + void this.sync({ reason: "session-delta" }).catch((err: unknown) => { log.warn(`memory sync failed (session-delta): ${String(err)}`); }); } diff --git a/extensions/memory-core/src/memory/manager.ts b/extensions/memory-core/src/memory/manager.ts index 909e47eb58e..1b8402e551b 100644 --- a/extensions/memory-core/src/memory/manager.ts +++ b/extensions/memory-core/src/memory/manager.ts @@ -369,7 +369,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem if (key && this.sessionWarm.has(key)) { return; } - void this.sync({ reason: "session-start" }).catch((err) => { + void this.sync({ reason: "session-start" }).catch((err: unknown) => { log.warn(`memory sync failed (session-start): ${String(err)}`); }); if (key) { @@ -471,7 +471,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem boostFallbackRanking: true, }, sourceFilterList, - ).catch((err) => { + ).catch((err: unknown) => { log.warn(`memory search: FTS keyword query failed: ${formatErrorMessage(err)}`); return []; }); @@ -492,7 +492,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem candidates, { boostFallbackRanking: true }, sourceFilterList, - ).catch((err) => { + ).catch((err: unknown) => { log.warn( `memory search: FTS per-keyword query failed for "${term}": ${formatErrorMessage(err)}`, ); @@ -531,7 +531,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem candidates, { boostFallbackRanking: true }, sourceFilterList, - ).catch((err) => { + ).catch((err: unknown) => { log.warn(`memory search: FTS hybrid keyword query failed: ${formatErrorMessage(err)}`); return []; }) @@ -564,7 +564,7 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem } const hasVector = queryVec.some((v) => v !== 0); const vectorResults = hasVector - ? await this.searchVector(queryVec, candidates, sourceFilterList).catch((err) => { + ? await this.searchVector(queryVec, candidates, sourceFilterList).catch((err: unknown) => { log.warn(`memory search: vector query failed: ${formatErrorMessage(err)}`); return []; }) @@ -1108,7 +1108,21 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem } const closeError = closeErrors.values().next().value; if (closeError) { - throw closeError; + throw toLintErrorObject(closeError, "Non-Error thrown"); } } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/memory-core/src/memory/qmd-manager.ts b/extensions/memory-core/src/memory/qmd-manager.ts index d1d80bb354b..ed902ae075a 100644 --- a/extensions/memory-core/src/memory/qmd-manager.ts +++ b/extensions/memory-core/src/memory/qmd-manager.ts @@ -479,18 +479,18 @@ export class QmdMemoryManager implements MemorySearchManager { if (this.qmd.update.onBoot) { const bootRun = this.runUpdate("boot", true); if (this.qmd.update.waitForBootSync) { - await bootRun.catch((err) => { + await bootRun.catch((err: unknown) => { log.warn(`qmd boot update failed: ${String(err)}`); }); } else { - void bootRun.catch((err) => { + void bootRun.catch((err: unknown) => { log.warn(`qmd boot update failed: ${String(err)}`); }); } } if (this.qmd.update.intervalMs > 0) { this.updateTimer = setInterval(() => { - void this.runUpdate("interval").catch((err) => { + void this.runUpdate("interval").catch((err: unknown) => { log.warn(`qmd update failed (${String(err)})`); }); }, this.qmd.update.intervalMs); @@ -498,7 +498,7 @@ export class QmdMemoryManager implements MemorySearchManager { if (this.shouldScheduleEmbedTimer()) { const startPeriodicEmbedTimer = () => { this.embedTimer = setInterval(() => { - void this.runUpdate("embed-interval").catch((err) => { + void this.runUpdate("embed-interval").catch((err: unknown) => { log.warn(`qmd embed interval update failed (${String(err)})`); }); }, this.qmd.update.embedIntervalMs); @@ -511,7 +511,7 @@ export class QmdMemoryManager implements MemorySearchManager { return; } void this.runUpdate("embed-interval") - .catch((err) => { + .catch((err: unknown) => { log.warn(`qmd embed interval update failed (${String(err)})`); }) .finally(() => { @@ -1650,7 +1650,7 @@ export class QmdMemoryManager implements MemorySearchManager { return; } await this.sync({ reason: "watch" }); - })().catch((err) => { + })().catch((err: unknown) => { log.warn(`qmd watch sync failed: ${String(err)}`); }); }, this.syncSettings.watchDebounceMs); @@ -1665,7 +1665,7 @@ export class QmdMemoryManager implements MemorySearchManager { return; } this.sessionWarm.add(key); - void this.sync({ reason: "session-start" }).catch((err) => { + void this.sync({ reason: "session-start" }).catch((err: unknown) => { log.warn(`qmd session-start sync failed: ${String(err)}`); }); } @@ -2070,8 +2070,6 @@ export class QmdMemoryManager implements MemorySearchManager { case "vsearch": // Vector search only return [{ type: "vec", query: semanticQuery }]; - case "query": - case undefined: default: // Full hybrid: lex + vec + hyde (query expansion) return [ diff --git a/extensions/memory-core/src/memory/search-manager.ts b/extensions/memory-core/src/memory/search-manager.ts index ed96cf6a07e..048444da16e 100644 --- a/extensions/memory-core/src/memory/search-manager.ts +++ b/extensions/memory-core/src/memory/search-manager.ts @@ -293,7 +293,7 @@ export async function getMemorySearchManager(params: { } QMD_MANAGER_CACHE.set(scopeKey, created.entry); if (cached) { - await closeQmdManagerForReplacement(cached.manager).catch((err) => { + await closeQmdManagerForReplacement(cached.manager).catch((err: unknown) => { log.warn(`failed to retire replaced qmd memory manager: ${formatErrorMessage(err)}`); }); } diff --git a/extensions/memory-core/src/short-term-promotion.ts b/extensions/memory-core/src/short-term-promotion.ts index 0db3f3bfa8d..bba9f6f2f7c 100644 --- a/extensions/memory-core/src/short-term-promotion.ts +++ b/extensions/memory-core/src/short-term-promotion.ts @@ -751,7 +751,7 @@ async function ensureShortTermArtifactsDir(workspaceDir: string): Promise const ensuring = fs .mkdir(artifactsDir, { recursive: true }) .then(() => undefined) - .catch((err) => { + .catch((err: unknown) => { ensuredShortTermDirs.delete(artifactsDir); throw err; }); diff --git a/extensions/memory-lancedb/index.ts b/extensions/memory-lancedb/index.ts index cca3ac70c9a..821706b8561 100644 --- a/extensions/memory-lancedb/index.ts +++ b/extensions/memory-lancedb/index.ts @@ -229,7 +229,7 @@ class MemoryDB { return this.initPromise; } - this.initPromise = this.doInitialize().catch((error) => { + this.initPromise = this.doInitialize().catch((error: unknown) => { this.initPromise = null; throw error; }); @@ -403,7 +403,7 @@ class ProviderAdapterEmbeddings implements Embeddings { private getProvider(): Promise { // Auth profiles and local providers can be repaired while the Gateway stays up. // Cache successful setup, but retry after failed provider discovery/auth. - this.providerPromise ??= this.createProvider().catch((err) => { + this.providerPromise ??= this.createProvider().catch((err: unknown) => { this.providerPromise = undefined; throw err; }); diff --git a/extensions/memory-lancedb/lancedb-runtime.ts b/extensions/memory-lancedb/lancedb-runtime.ts index 4eff4279198..45108515d65 100644 --- a/extensions/memory-lancedb/lancedb-runtime.ts +++ b/extensions/memory-lancedb/lancedb-runtime.ts @@ -51,7 +51,7 @@ export function createLanceDbRuntimeLoader(overrides: Partial { if (!loadPromise) { - loadPromise = deps.importBundled().catch((error) => { + loadPromise = deps.importBundled().catch((error: unknown) => { loadPromise = null; if (isUnsupportedNativePlatform({ platform: deps.platform, arch: deps.arch })) { throw new Error( diff --git a/extensions/memory-wiki/src/import-runs.ts b/extensions/memory-wiki/src/import-runs.ts index af8672e6ea0..c0ea8758782 100644 --- a/extensions/memory-wiki/src/import-runs.ts +++ b/extensions/memory-wiki/src/import-runs.ts @@ -114,8 +114,9 @@ export async function listMemoryWikiImportRuns( const importRunsDir = resolveImportRunsDir(config.vault.path); const entries = await fs .readdir(importRunsDir, { withFileTypes: true }) - .catch((error: NodeJS.ErrnoException) => { - if (error?.code === "ENOENT") { + .catch((error: unknown) => { + const code = asRecord(error)?.code; + if (code === "ENOENT") { return []; } throw error; diff --git a/extensions/minimax/video-generation-provider.ts b/extensions/minimax/video-generation-provider.ts index 9a7bb969a79..13a9e99a161 100644 --- a/extensions/minimax/video-generation-provider.ts +++ b/extensions/minimax/video-generation-provider.ts @@ -209,8 +209,6 @@ async function pollMinimaxVideo(params: { normalizeOptionalString(payload.base_resp?.status_msg) || "MiniMax video generation failed", ); - case "Preparing": - case "Processing": default: await waitProviderOperationPollInterval({ deadline, pollIntervalMs: POLL_INTERVAL_MS }); break; diff --git a/extensions/moonshot/moonshot.live.test.ts b/extensions/moonshot/moonshot.live.test.ts index 9c82c96e9da..1e9e593c19c 100644 --- a/extensions/moonshot/moonshot.live.test.ts +++ b/extensions/moonshot/moonshot.live.test.ts @@ -45,7 +45,7 @@ describeLive("moonshot plugin live", () => { } } if (lastError) { - throw lastError; + throw toLintErrorObject(lastError, "Non-Error thrown"); } expect(result?.provider).toBe("kimi"); @@ -54,3 +54,17 @@ describeLive("moonshot plugin live", () => { expect(Array.isArray(result?.citations)).toBe(true); }, 180_000); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/msteams/src/feedback-invoke.ts b/extensions/msteams/src/feedback-invoke.ts index 69bcee25da8..29abfecba7f 100644 --- a/extensions/msteams/src/feedback-invoke.ts +++ b/extensions/msteams/src/feedback-invoke.ts @@ -191,7 +191,7 @@ export async function runMSTeamsFeedbackInvokeHandler( feedbackMessageId: messageId, userComment, log: deps.log, - }).catch((err) => { + }).catch((err: unknown) => { deps.log.error("feedback reflection failed", { error: formatUnknownError(err) }); }); } diff --git a/extensions/msteams/src/monitor-handler/message-handler.ts b/extensions/msteams/src/monitor-handler/message-handler.ts index 709be99c51f..4b0969b3472 100644 --- a/extensions/msteams/src/monitor-handler/message-handler.ts +++ b/extensions/msteams/src/monitor-handler/message-handler.ts @@ -326,7 +326,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { allowNameMatching, }); if (senderAccess.decision === "pairing") { - conversationStore.upsert(conversationId, conversationRef).catch((err) => { + conversationStore.upsert(conversationId, conversationRef).catch((err: unknown) => { log.debug?.("failed to save conversation reference", { error: formatUnknownError(err), }); @@ -433,7 +433,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { return; } - conversationStore.upsert(conversationId, conversationRef).catch((err) => { + conversationStore.upsert(conversationId, conversationRef).catch((err: unknown) => { log.debug?.("failed to save conversation reference", { error: formatUnknownError(err), }); diff --git a/extensions/msteams/src/monitor.ts b/extensions/msteams/src/monitor.ts index 8194a06651e..f621f09b53b 100644 --- a/extensions/msteams/src/monitor.ts +++ b/extensions/msteams/src/monitor.ts @@ -620,7 +620,7 @@ export async function monitorMSTeamsProvider( const onError = (err: unknown) => { httpServer.off("listening", onListening); log.error("msteams server error", { error: formatUnknownError(err) }); - reject(err); + reject(toLintErrorObject(err, "MSTeams server failed")); }; httpServer.once("listening", onListening); httpServer.once("error", onError); @@ -653,6 +653,20 @@ export async function monitorMSTeamsProvider( return { app: expressApp, shutdown }; } +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} + /** * Build a minimal ActivityHandler-compatible object that supports * onMessage / onMembersAdded registration and a run() method. diff --git a/extensions/msteams/src/reply-dispatcher.ts b/extensions/msteams/src/reply-dispatcher.ts index d2ec83f48cb..80bfce031d1 100644 --- a/extensions/msteams/src/reply-dispatcher.ts +++ b/extensions/msteams/src/reply-dispatcher.ts @@ -344,7 +344,7 @@ export function createMSTeamsReplyDispatcher(params: { const markDispatchIdle = (): Promise => { return flushPendingMessages() - .catch((err) => { + .catch((err: unknown) => { const errMsg = formatUnknownError(err); const classification = classifyMSTeamsSendError(err); const hint = formatMSTeamsSendErrorHint(classification); @@ -356,7 +356,7 @@ export function createMSTeamsReplyDispatcher(params: { }); }) .then(async () => { - const fallbackPayload = await streamController.finalize().catch((err) => { + const fallbackPayload = await streamController.finalize().catch((err: unknown) => { params.log.debug?.("stream finalize failed", { error: formatUnknownError(err) }); return undefined; }); diff --git a/extensions/nostr/src/nostr-bus.ts b/extensions/nostr/src/nostr-bus.ts index b84cb00b41e..6b868b442fe 100644 --- a/extensions/nostr/src/nostr-bus.ts +++ b/extensions/nostr/src/nostr-bus.ts @@ -423,7 +423,7 @@ export async function startNostrBus(options: NostrBusOptions): Promise onError?.(err as Error, "persist state")); + }).catch((err: unknown) => onError?.(err as Error, "persist state")); }, STATE_PERSIST_DEBOUNCE_MS); } @@ -698,7 +698,7 @@ export async function startNostrBus(options: NostrBusOptions): Promise { relayAbort.abort("closed by caller"); void Promise.resolve(sub.close("closed by caller")) - .catch((err) => onError?.(err as Error, "close subscription")) + .catch((err: unknown) => onError?.(err as Error, "close subscription")) .finally(() => { pool.close(relays); }); @@ -713,7 +713,7 @@ export async function startNostrBus(options: NostrBusOptions): Promise onError?.(err as Error, "persist state on close")); + }).catch((err: unknown) => onError?.(err as Error, "persist state on close")); } }, publicKey: pk, diff --git a/extensions/ollama/src/setup.ts b/extensions/ollama/src/setup.ts index c58fac7ff07..2d2a1e44704 100644 --- a/extensions/ollama/src/setup.ts +++ b/extensions/ollama/src/setup.ts @@ -203,10 +203,10 @@ async function readOllamaPullChunkWithIdleTimeout( resolve(result); } }, - (err) => { + (err: unknown) => { clear(); if (!timedOut) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } }, ); @@ -742,3 +742,17 @@ export async function ensureOllamaModelPulled(params: { throw new WizardCancelledError("Failed to download selected Ollama model"); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/openai/openai-chatgpt-oauth-flow.runtime.ts b/extensions/openai/openai-chatgpt-oauth-flow.runtime.ts index 14519118d2b..df8dce69a3c 100644 --- a/extensions/openai/openai-chatgpt-oauth-flow.runtime.ts +++ b/extensions/openai/openai-chatgpt-oauth-flow.runtime.ts @@ -448,7 +448,7 @@ export async function loginOpenAICodex(options: { manualCode = input; server.cancelWait(); }) - .catch((err) => { + .catch((err: unknown) => { manualError = err instanceof Error ? err : new Error(String(err)); server.cancelWait(); }); @@ -480,7 +480,7 @@ export async function loginOpenAICodex(options: { if (!code) { await withOAuthLoginAbort(manualPromise, options.signal, server.cancelWait); if (manualError) { - throw manualError; + throw toLintErrorObject(manualError, "Non-Error thrown"); } if (manualCode) { const parsed = parseOAuthAuthorizationInput(manualCode); @@ -605,3 +605,17 @@ export const testing = { resolveCallbackHost, resolveRedirectUri, }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/qa-channel/src/bus-client.ts b/extensions/qa-channel/src/bus-client.ts index d20feab5f6e..76a3554f3dd 100644 --- a/extensions/qa-channel/src/bus-client.ts +++ b/extensions/qa-channel/src/bus-client.ts @@ -79,7 +79,7 @@ async function postJson( try { parsed = text ? (JSON.parse(text) as T | { error?: string }) : ({} as T); } catch (error) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); return; } if ((response.statusCode ?? 500) < 200 || (response.statusCode ?? 500) >= 300) { @@ -295,3 +295,17 @@ export async function getQaBusState(baseUrl: string): Promise((resolve, reject) => { execFile(command, args, { cwd }, (error, stdout, stderr) => { if (error) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); return; } resolve({ stdout, stderr }); @@ -361,3 +361,17 @@ export async function buildQaDockerHarnessImage( return { imageName }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/qa-lab/src/gateway-child.ts b/extensions/qa-lab/src/gateway-child.ts index 1fdd8b4813a..a8997afb168 100644 --- a/extensions/qa-lab/src/gateway-child.ts +++ b/extensions/qa-lab/src/gateway-child.ts @@ -96,7 +96,7 @@ async function closeWriteStream(stream: WriteStream) { } async function writeSanitizedQaGatewayDebugLog(params: { sourcePath: string; targetPath: string }) { - const contents = await fs.readFile(params.sourcePath, "utf8").catch((error) => { + const contents = await fs.readFile(params.sourcePath, "utf8").catch((error: unknown) => { if ((error as NodeJS.ErrnoException).code === "ENOENT") { return ""; } @@ -783,7 +783,10 @@ export async function startQaGatewayChild(params: { } } if (!rpcReady) { - throw lastRpcStartupError ?? new Error("qa gateway rpc client failed to start"); + throw toLintErrorObject( + lastRpcStartupError ?? new Error("qa gateway rpc client failed to start"), + "Non-Error thrown", + ); } } catch (error) { await attemptRpcClient.stop().catch(() => {}); @@ -883,7 +886,10 @@ export async function startQaGatewayChild(params: { } } if (!rpcReady) { - throw lastRpcStartupError ?? new Error("qa gateway rpc client failed to start"); + throw toLintErrorObject( + lastRpcStartupError ?? new Error("qa gateway rpc client failed to start"), + "Non-Error thrown", + ); } } catch (error) { await nextRpcClient.stop().catch(() => {}); @@ -1035,3 +1041,17 @@ export async function startQaGatewayChild(params: { } } export { testing as __testing }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts b/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts index 02fa3a422bd..e0b0fea13e2 100644 --- a/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts +++ b/extensions/qa-lab/src/live-transports/slack/slack-live.runtime.ts @@ -1760,9 +1760,11 @@ async function preserveSlackGatewayDebugArtifacts(params: { gatewayDebugDirPath: string; gatewayHarness: SlackQaGatewayHarness; }) { - await params.gatewayHarness.stop({ preserveToDir: params.gatewayDebugDirPath }).catch((error) => { - appendLiveLaneIssue(params.cleanupIssues, "gateway debug preservation failed", error); - }); + await params.gatewayHarness + .stop({ preserveToDir: params.gatewayDebugDirPath }) + .catch((error: unknown) => { + appendLiveLaneIssue(params.cleanupIssues, "gateway debug preservation failed", error); + }); } export async function runSlackQaLive(params: { @@ -2027,7 +2029,7 @@ export async function runSlackQaLive(params: { break; } finally { if (!preservedGatewayDebugArtifacts && gatewayHarness) { - await gatewayHarness.stop().catch((error) => { + await gatewayHarness.stop().catch((error: unknown) => { appendLiveLaneIssue(cleanupIssues, "gateway stop failed", error); }); await new Promise((resolve) => { diff --git a/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts b/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts index 3f79c2e481d..5b40a596f50 100644 --- a/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts +++ b/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts @@ -1390,7 +1390,7 @@ export async function runWhatsAppQaLive(params: { } } if (tempAuthRoot) { - await fs.rm(tempAuthRoot, { recursive: true, force: true }).catch((error) => { + await fs.rm(tempAuthRoot, { recursive: true, force: true }).catch((error: unknown) => { appendLiveLaneIssue(cleanupIssues, "temporary auth cleanup failed", error); }); } diff --git a/extensions/qa-lab/src/providers/server-runtime.ts b/extensions/qa-lab/src/providers/server-runtime.ts index 90634be4a3a..cb5590aff5a 100644 --- a/extensions/qa-lab/src/providers/server-runtime.ts +++ b/extensions/qa-lab/src/providers/server-runtime.ts @@ -29,7 +29,6 @@ export async function startQaProviderServer( return await startMockOpenAiProviderServer(serverParams); case "aimock": return await startAimockProviderServer(serverParams); - case "live-frontier": default: return null; } diff --git a/extensions/qa-lab/src/suite-runtime-gateway.ts b/extensions/qa-lab/src/suite-runtime-gateway.ts index 84461c0ef62..0527064a365 100644 --- a/extensions/qa-lab/src/suite-runtime-gateway.ts +++ b/extensions/qa-lab/src/suite-runtime-gateway.ts @@ -289,7 +289,10 @@ async function runConfigMutation(params: { return { ok: true, restarted: true }; } } - throw lastConflict ?? new Error(`${params.action} failed after retrying config hash conflicts`); + throw toLintErrorObject( + lastConflict ?? new Error(`${params.action} failed after retrying config hash conflicts`), + "Non-Error thrown", + ); } async function patchConfig(params: { @@ -354,3 +357,17 @@ export { waitForQaChannelReady, waitForTransportReady, }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/qa-lab/src/suite.ts b/extensions/qa-lab/src/suite.ts index 2bf3dd1cbea..e280e8ee81f 100644 --- a/extensions/qa-lab/src/suite.ts +++ b/extensions/qa-lab/src/suite.ts @@ -1142,7 +1142,7 @@ export async function runQaSuite(params?: QaSuiteRunParams): Promise { + .catch((error: unknown) => { writeQaSuiteProgress( progressEnabled, `partial artifact write failed: ${sanitizeQaSuiteProgressValue(formatErrorMessage(error))}`, diff --git a/extensions/qa-matrix/src/cli.runtime.ts b/extensions/qa-matrix/src/cli.runtime.ts index 59acd5318ff..de9ee3c9c1b 100644 --- a/extensions/qa-matrix/src/cli.runtime.ts +++ b/extensions/qa-matrix/src/cli.runtime.ts @@ -83,9 +83,23 @@ export async function runQaMatrixCommand(opts: LiveTransportQaCommandOptions) { `Matrix QA output log error: ${formatMatrixQaOutputTeeError(outputTeeError)}\n`, ); } - throw primaryError; + throw toLintErrorObject(primaryError, "Non-Error thrown"); } if (outputTeeError) { - throw outputTeeError; + throw toLintErrorObject(outputTeeError, "Non-Error thrown"); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/qa-matrix/src/runners/contract/scenario-runtime-cli.ts b/extensions/qa-matrix/src/runners/contract/scenario-runtime-cli.ts index 38435b060b7..58fc8f5c501 100644 --- a/extensions/qa-matrix/src/runners/contract/scenario-runtime-cli.ts +++ b/extensions/qa-matrix/src/runners/contract/scenario-runtime-cli.ts @@ -255,7 +255,7 @@ export function startMatrixQaOpenClawCli(params: { return; } settleWait = { reject, resolve }; - }).catch((error) => { + }).catch((error: unknown) => { throw new Error( `Matrix QA CLI command failed (${formatMatrixQaCliCommand(params.args)}): ${redactMatrixQaCliOutput(formatErrorMessage(error))}`, ); diff --git a/extensions/qqbot/src/engine/api/token.ts b/extensions/qqbot/src/engine/api/token.ts index 7beb7a17cee..299da9a0b4f 100644 --- a/extensions/qqbot/src/engine/api/token.ts +++ b/extensions/qqbot/src/engine/api/token.ts @@ -190,9 +190,11 @@ export class TokenManager { this.logger?.info?.(`[qqbot:token:${appId}] Background refresh stopped`); }; - loop().catch((err) => { + loop().catch((err: unknown) => { this.refreshControllers.delete(appId); - this.logger?.error?.(`[qqbot:token:${appId}] Background refresh crashed: ${err}`); + this.logger?.error?.( + `[qqbot:token:${appId}] Background refresh crashed: ${formatErrorMessage(err)}`, + ); }); } diff --git a/extensions/qqbot/src/engine/gateway/interaction-handler.ts b/extensions/qqbot/src/engine/gateway/interaction-handler.ts index af63d00cd37..e456faf6cfa 100644 --- a/extensions/qqbot/src/engine/gateway/interaction-handler.ts +++ b/extensions/qqbot/src/engine/gateway/interaction-handler.ts @@ -199,7 +199,7 @@ export function createInteractionHandler( // ---- Approval button / other ---- const parsed = parseApprovalButtonData(event.data?.resolved?.button_data ?? ""); if (!parsed) { - void acknowledgeInteraction(creds, event.id).catch((err) => { + void acknowledgeInteraction(creds, event.id).catch((err: unknown) => { log?.error(`Interaction ACK failed: ${err instanceof Error ? err.message : String(err)}`); }); return; diff --git a/extensions/qqbot/src/engine/gateway/message-queue.ts b/extensions/qqbot/src/engine/gateway/message-queue.ts index 846b67eaf37..447bcb78451 100644 --- a/extensions/qqbot/src/engine/gateway/message-queue.ts +++ b/extensions/qqbot/src/engine/gateway/message-queue.ts @@ -387,8 +387,8 @@ export function createMessageQueue(ctx: MessageQueueContext): MessageQueue { const executeImmediate = (msg: QueuedMessage): void => { if (handleMessageFnRef) { - handleMessageFnRef(msg).catch((err) => { - log?.error(`Immediate execution error: ${err}`); + handleMessageFnRef(msg).catch((err: unknown) => { + log?.error(`Immediate execution error: ${formatErrorMessage(err)}`); }); } }; diff --git a/extensions/qqbot/src/engine/messaging/streaming-c2c.ts b/extensions/qqbot/src/engine/messaging/streaming-c2c.ts index 92232843a53..f7fc2abf254 100644 --- a/extensions/qqbot/src/engine/messaging/streaming-c2c.ts +++ b/extensions/qqbot/src/engine/messaging/streaming-c2c.ts @@ -480,7 +480,7 @@ export class StreamingController { // 将实际逻辑挂到 Promise 链尾部,保证串行执行 this.callbackChain = this.callbackChain.then( () => this.handlePartialReply(payload), - (err) => { + (err: unknown) => { // 上一次如果异常,不阻塞后续调用 this.logError(`onPartialReply chain error: ${formatStreamErr(err)}`); return this.handlePartialReply(payload); @@ -586,7 +586,7 @@ export class StreamingController { // 挂到串行队列尾部,等所有 onPartialReply 执行完再处理 this.callbackChain = this.callbackChain.then( () => this.handleIdle(payload), - (err) => { + (err: unknown) => { this.logError(`onIdle chain error: ${formatStreamErr(err)}`); return this.handleIdle(payload); }, diff --git a/extensions/qqbot/src/engine/utils/audio.ts b/extensions/qqbot/src/engine/utils/audio.ts index c449a4ed473..04cea695c98 100644 --- a/extensions/qqbot/src/engine/utils/audio.ts +++ b/extensions/qqbot/src/engine/utils/audio.ts @@ -24,7 +24,7 @@ function loadSilkWasm(): Promise { if (silkWasmPromise) { return silkWasmPromise; } - silkWasmPromise = import("silk-wasm").catch((err) => { + silkWasmPromise = import("silk-wasm").catch((err: unknown) => { debugWarn( `[audio-convert] silk-wasm not available; SILK encode/decode disabled (${formatErrorMessage(err)})`, ); diff --git a/extensions/runway/video-generation-provider.ts b/extensions/runway/video-generation-provider.ts index e86da901c41..6c236ccc8cf 100644 --- a/extensions/runway/video-generation-provider.ts +++ b/extensions/runway/video-generation-provider.ts @@ -310,9 +310,6 @@ async function pollRunwayTask(params: { readRunwayFailureMessage(payload.failure) || `Runway video generation ${normalizeLowercaseStringOrEmpty(status)}`, ); - case "PENDING": - case "RUNNING": - case "THROTTLED": default: await waitProviderOperationPollInterval({ deadline, pollIntervalMs: POLL_INTERVAL_MS }); break; diff --git a/extensions/signal/src/client-container.ts b/extensions/signal/src/client-container.ts index 958ae4c9ad3..0181984e9f0 100644 --- a/extensions/signal/src/client-container.ts +++ b/extensions/signal/src/client-container.ts @@ -298,7 +298,7 @@ export async function streamContainerEvents(params: { logError( `[signal-ws] failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`, ); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); return; } @@ -717,3 +717,17 @@ export async function containerRpcRequest( throw new Error(`Unsupported container RPC method: ${method}`); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/signal/src/client.ts b/extensions/signal/src/client.ts index 5c80b54bec6..793d3ec8a6a 100644 --- a/extensions/signal/src/client.ts +++ b/extensions/signal/src/client.ts @@ -139,7 +139,7 @@ function requestSignalHttpText( } settled = true; cleanup(); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }; const resolveOnce = (response: SignalHttpResponse) => { if (settled) { @@ -291,7 +291,7 @@ function openSignalEventStream( } settled = true; cleanup(); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }; const request: ClientRequest = client.request( url, @@ -432,3 +432,17 @@ export async function streamSignalEvents(params: { flushEvent(); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/signal/src/monitor.tool-result.autostart.test.ts b/extensions/signal/src/monitor.tool-result.autostart.test.ts index 23b3fe851cf..da7741d70e3 100644 --- a/extensions/signal/src/monitor.tool-result.autostart.test.ts +++ b/extensions/signal/src/monitor.tool-result.autostart.test.ts @@ -177,12 +177,18 @@ describe("monitorSignalProvider autostart", () => { async (params: { abortSignal?: AbortSignal | null }) => { await new Promise((_resolve, reject) => { if (params.abortSignal?.aborted) { - reject(params.abortSignal.reason); + reject(toLintErrorObject(params.abortSignal.reason, "Non-Error rejection")); return; } params.abortSignal?.addEventListener( "abort", - () => reject(params.abortSignal?.reason ?? new Error("aborted")), + () => + reject( + toLintErrorObject( + params.abortSignal?.reason ?? new Error("aborted"), + "Non-Error rejection", + ), + ), { once: true }, ); }); @@ -238,3 +244,17 @@ describe("monitorSignalProvider autostart", () => { ).resolves.toBeUndefined(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/signal/src/monitor.ts b/extensions/signal/src/monitor.ts index 9ce6279619f..51a04e66280 100644 --- a/extensions/signal/src/monitor.ts +++ b/extensions/signal/src/monitor.ts @@ -85,7 +85,7 @@ function createSignalMonitorTaskRunner(runtime: RuntimeEnv) { runEventTask(task: () => Promise): void { const trackedTask = Promise.resolve() .then(task) - .catch((err) => runtime.error?.(`event handler failed: ${String(err)}`)) + .catch((err: unknown) => runtime.error?.(`event handler failed: ${String(err)}`)) .finally(() => inFlight.delete(trackedTask)); inFlight.add(trackedTask); }, diff --git a/extensions/slack/src/monitor/media.ts b/extensions/slack/src/monitor/media.ts index fbd9df395e1..796c9bfae75 100644 --- a/extensions/slack/src/monitor/media.ts +++ b/extensions/slack/src/monitor/media.ts @@ -209,7 +209,7 @@ async function saveSlackMedia(params: { }, } : {}), - }).catch((error) => { + }).catch((error: unknown) => { if (timedOut) { return new Promise(() => {}); } diff --git a/extensions/slack/src/monitor/message-handler/dispatch.ts b/extensions/slack/src/monitor/message-handler/dispatch.ts index af7f35573ce..12bf42eaa40 100644 --- a/extensions/slack/src/monitor/message-handler/dispatch.ts +++ b/extensions/slack/src/monitor/message-handler/dispatch.ts @@ -532,7 +532,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag await reactSlackMessage(message.channel, reactionMessageTs ?? "", toSlackEmojiName(emoji), { token: ctx.botToken, client: ctx.app.client, - }).catch((err) => { + }).catch((err: unknown) => { if (formatErrorMessage(err).includes("already_reacted")) { return; } @@ -543,7 +543,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag await removeSlackReaction(message.channel, reactionMessageTs ?? "", toSlackEmojiName(emoji), { token: ctx.botToken, client: ctx.app.client, - }).catch((err) => { + }).catch((err: unknown) => { if (formatErrorMessage(err).includes("no_reaction")) { return; } @@ -1866,7 +1866,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag } if (dispatchError) { - throw dispatchError; + throw toLintErrorObject(dispatchError, "Slack dispatch failed"); } // Record thread participation only when we actually delivered a reply and @@ -1916,3 +1916,17 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag }); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/slack/src/monitor/message-handler/prepare.ts b/extensions/slack/src/monitor/message-handler/prepare.ts index b6dcd6e9330..36a085da78c 100644 --- a/extensions/slack/src/monitor/message-handler/prepare.ts +++ b/extensions/slack/src/monitor/message-handler/prepare.ts @@ -1107,7 +1107,7 @@ export async function prepareSlackMessage(params: { client: ctx.app.client, }).then( () => true, - (err) => { + (err: unknown) => { logVerbose( `slack react failed for channel ${message.channel}: ${formatSlackError(err)}`, ); diff --git a/extensions/slack/src/monitor/provider-support.ts b/extensions/slack/src/monitor/provider-support.ts index 06aafb8da33..4ba2ce0bc99 100644 --- a/extensions/slack/src/monitor/provider-support.ts +++ b/extensions/slack/src/monitor/provider-support.ts @@ -104,7 +104,7 @@ function installSlackNativeReconnectFailureObserver(receiver: unknown) { resolve(undefined); return; } - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }); }, delayMs); }); @@ -392,10 +392,10 @@ export async function startSlackSocketAndWaitForDisconnect(params: { await Promise.resolve(); const disconnect = disconnectWaiter.getLatest(); disconnectWaiter.cancel(); - if ((err === undefined || err === null || err === "") && disconnect?.error !== undefined) { - throw disconnect.error; + if (isMissingSocketStartErrorDetail(err) && disconnect?.error !== undefined) { + throw toLintErrorObject(disconnect.error, "Non-Error thrown"); } - if (err === undefined || err === null || err === "") { + if (isMissingSocketStartErrorDetail(err)) { const suffix = disconnect ? ` after ${disconnect.event}` : ""; throw new Error(`Slack Socket Mode start failed${suffix} without error detail`, { cause: err, @@ -405,6 +405,12 @@ export async function startSlackSocketAndWaitForDisconnect(params: { } } +function isMissingSocketStartErrorDetail(err: unknown): boolean { + return ( + err === undefined || err === null || err === "" || (err instanceof Error && err.message === "") + ); +} + export function resolveSlackSocketShutdownClient( app: unknown, ): SlackSocketShutdownClient | undefined { @@ -461,3 +467,17 @@ export function formatSlackUserResolved(entry: SlackUserResolution): string { extra: entry.note ? [entry.note] : [], }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/slack/src/monitor/provider.reconnect.test.ts b/extensions/slack/src/monitor/provider.reconnect.test.ts index 0ebcb044bf9..7f2895ade51 100644 --- a/extensions/slack/src/monitor/provider.reconnect.test.ts +++ b/extensions/slack/src/monitor/provider.reconnect.test.ts @@ -252,9 +252,9 @@ describe("slack socket reconnect helpers", () => { const err = new Error("missing_scope"); const app = { receiver: { client }, - start: vi.fn().mockImplementation(async () => { + start: vi.fn().mockImplementation(() => { client.emit("unable_to_socket_mode_start", err); - throw undefined; + throw new Error(); }), }; diff --git a/extensions/telegram/src/bot-handlers.runtime.ts b/extensions/telegram/src/bot-handlers.runtime.ts index 7e9c20e5592..3eb4fd4db96 100644 --- a/extensions/telegram/src/bot-handlers.runtime.ts +++ b/extensions/telegram/src/bot-handlers.runtime.ts @@ -568,7 +568,7 @@ export const registerTelegramHandlers = ({ "Something went wrong while processing your message. Please try again.", threadId != null ? { message_thread_id: threadId } : undefined, ) - .catch((sendErr) => { + .catch((sendErr: unknown) => { logVerbose(`telegram: error fallback send failed: ${String(sendErr)}`); }); } diff --git a/extensions/telegram/src/bot-message-context.ts b/extensions/telegram/src/bot-message-context.ts index 9a727f1c8cc..871c1f8a25d 100644 --- a/extensions/telegram/src/bot-message-context.ts +++ b/extensions/telegram/src/bot-message-context.ts @@ -490,7 +490,7 @@ export const buildTelegramMessageContext = async ({ // expensive context/session construction without showing typing for dropped turns. if (!isGroup) { initialTypingCueSent = true; - void sendTyping().catch((err) => { + void sendTyping().catch((err: unknown) => { logVerbose(`telegram early direct typing cue failed for chat ${chatId}: ${String(err)}`); }); } @@ -585,7 +585,7 @@ export const buildTelegramMessageContext = async ({ chat: msg.chat, chatId, getChat: getChatApi ?? undefined, - }).catch((err) => { + }).catch((err: unknown) => { logVerbose( `telegram status-reaction available_reactions lookup failed for chat ${chatId}: ${String(err)}`, ); @@ -630,7 +630,7 @@ export const buildTelegramMessageContext = async ({ reactionApi(chatId, msg.message_id, [{ type: "emoji", emoji: ackReactionEmoji }]), }).then( () => true, - (err) => { + (err: unknown) => { logVerbose(`telegram react failed for chat ${chatId}: ${String(err)}`); return false; }, diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts index e845e78373b..b5ad1dac8ab 100644 --- a/extensions/telegram/src/bot-message-dispatch.ts +++ b/extensions/telegram/src/bot-message-dispatch.ts @@ -1071,7 +1071,7 @@ export const dispatchTelegramMessage = async ({ } await task(); }); - draftLaneEventQueue = next.catch((err) => { + draftLaneEventQueue = next.catch((err: unknown) => { logVerbose(`telegram: draft lane callback failed: ${String(err)}`); }); return draftLaneEventQueue; diff --git a/extensions/telegram/src/bot-message.ts b/extensions/telegram/src/bot-message.ts index 9f8ccc9fd47..3150728eab5 100644 --- a/extensions/telegram/src/bot-message.ts +++ b/extensions/telegram/src/bot-message.ts @@ -164,7 +164,7 @@ export const createTelegramMessageProcessor = (deps: TelegramMessageProcessorDep context.ctxPayload.InboundEventKind !== "room_event" && context.initialTypingCueSent !== true ) { - void context.sendTyping().catch((err) => { + void context.sendTyping().catch((err: unknown) => { logVerbose(`telegram early typing cue failed for chat ${context.chatId}: ${String(err)}`); }); } diff --git a/extensions/telegram/src/bot-native-command-menu.ts b/extensions/telegram/src/bot-native-command-menu.ts index 44916d79180..b40e71ecd0d 100644 --- a/extensions/telegram/src/bot-native-command-menu.ts +++ b/extensions/telegram/src/bot-native-command-menu.ts @@ -586,7 +586,7 @@ export function syncTelegramMenuCommands(params: { writeCachedCommandHash(accountId, botIdentity, currentHash); }; - void sync().catch((err) => { + void sync().catch((err: unknown) => { runtime.error?.(`Telegram command sync failed: ${String(err)}`); }); } diff --git a/extensions/telegram/src/bot-update-tracker.ts b/extensions/telegram/src/bot-update-tracker.ts index c84c925a5c9..58c04d35d83 100644 --- a/extensions/telegram/src/bot-update-tracker.ts +++ b/extensions/telegram/src/bot-update-tracker.ts @@ -118,7 +118,7 @@ export function createTelegramUpdateTracker(options: TelegramUpdateTrackerOption } highestPersistenceRequestedUpdateId = updateId; persistTargetUpdateId = updateId; - void drainPersistQueue().catch((err) => { + void drainPersistQueue().catch((err: unknown) => { options.onPersistError?.(err); }); }; @@ -174,7 +174,7 @@ export function createTelegramUpdateTracker(options: TelegramUpdateTrackerOption if (!receiveContext?.shouldAckAfter(stage)) { return; } - void receiveContext.ack().catch((err) => { + void receiveContext.ack().catch((err: unknown) => { options.onPersistError?.(err); }); }; @@ -251,7 +251,7 @@ export function createTelegramUpdateTracker(options: TelegramUpdateTrackerOption failedUpdateIds.add(update.updateId); void update.receiveContext ?.nack(new Error("Telegram update handler did not complete")) - .catch((err) => { + .catch((err: unknown) => { options.onPersistError?.(err); }); } diff --git a/extensions/telegram/src/bot.fetch-abort.test.ts b/extensions/telegram/src/bot.fetch-abort.test.ts index 23db5faedea..eaf19549883 100644 --- a/extensions/telegram/src/bot.fetch-abort.test.ts +++ b/extensions/telegram/src/bot.fetch-abort.test.ts @@ -171,7 +171,11 @@ describe("createTelegramBot fetch abort", () => { (_input: RequestInfo | URL, init?: RequestInit) => new Promise((_resolve, reject) => { const signal = init?.signal as AbortSignal; - signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + signal.addEventListener( + "abort", + () => reject(toLintErrorObject(signal.reason, "Non-Error rejection")), + { once: true }, + ); }), ) .mockResolvedValueOnce({ ok: true } as Response); @@ -200,7 +204,11 @@ describe("createTelegramBot fetch abort", () => { (_input: RequestInfo | URL, init?: RequestInit) => new Promise((_resolve, reject) => { const signal = init?.signal as AbortSignal; - signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + signal.addEventListener( + "abort", + () => reject(toLintErrorObject(signal.reason, "Non-Error rejection")), + { once: true }, + ); }), ) .mockResolvedValueOnce({ ok: true } as Response); @@ -228,7 +236,11 @@ describe("createTelegramBot fetch abort", () => { (_input: RequestInfo | URL, init?: RequestInit) => new Promise((_resolve, reject) => { const signal = init?.signal as AbortSignal; - signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + signal.addEventListener( + "abort", + () => reject(toLintErrorObject(signal.reason, "Non-Error rejection")), + { once: true }, + ); }), ) .mockResolvedValueOnce({ ok: true } as Response); @@ -293,7 +305,7 @@ describe("createTelegramBot fetch abort", () => { }), ); const fetchSpy = vi.fn(async () => { - throw frozenError; + throw toLintErrorObject(frozenError, "Non-Error thrown"); }); const { clientFetch } = createWrappedTelegramClientFetch(fetchSpy as unknown as typeof fetch); @@ -303,3 +315,17 @@ describe("createTelegramBot fetch abort", () => { expect(getTelegramNetworkErrorOrigin(frozenError)).toBeNull(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/telegram/src/monitor.test.ts b/extensions/telegram/src/monitor.test.ts index 593c2ab0af5..adb5cd24eba 100644 --- a/extensions/telegram/src/monitor.test.ts +++ b/extensions/telegram/src/monitor.test.ts @@ -325,7 +325,7 @@ vi.mock("./bot.js", () => ({ createTelegramBotCalls.push(opts); const nextError = createTelegramBotErrors.shift(); if (nextError) { - throw nextError; + throw toLintErrorObject(nextError, "Non-Error thrown"); } const stop = vi.fn<() => void>(); createdBotStops.push(stop); @@ -1110,3 +1110,17 @@ describe("monitorTelegramProvider (grammY)", () => { expect(runSpy).not.toHaveBeenCalled(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/telegram/src/polling-session.test.ts b/extensions/telegram/src/polling-session.test.ts index 89bbc0672c8..59f9495a18d 100644 --- a/extensions/telegram/src/polling-session.test.ts +++ b/extensions/telegram/src/polling-session.test.ts @@ -225,7 +225,7 @@ function installPollingStallWatchdogHarness(dateNowSequence: readonly number[] = }, (error: unknown) => { realClearTimeout(timeout); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); @@ -3476,3 +3476,17 @@ describe("TelegramPollingSession", () => { expect(transport2.close).toHaveBeenCalled(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/telegram/src/polling-session.ts b/extensions/telegram/src/polling-session.ts index 68073641f1a..28a3d20a8ca 100644 --- a/extensions/telegram/src/polling-session.ts +++ b/extensions/telegram/src/polling-session.ts @@ -374,7 +374,7 @@ export class TelegramPollingSession { bypassBackoff: false, }), }) - .catch((err) => { + .catch((err: unknown) => { this.opts.log(`[telegram] reconnect delivery drain failed: ${formatErrorMessage(err)}`); }) .finally(() => { diff --git a/extensions/telegram/src/polling-transport-state.ts b/extensions/telegram/src/polling-transport-state.ts index 8857df3ca39..87aa2360d8d 100644 --- a/extensions/telegram/src/polling-transport-state.ts +++ b/extensions/telegram/src/polling-transport-state.ts @@ -67,7 +67,7 @@ export class TelegramPollingTransportState { // Fire-and-forget close used on the rebuild path so the polling cycle is not // blocked by a slow destroy. The error path is logged but never rethrown. #closeTransportAsync(transport: TelegramTransport, context: string) { - void transport.close().catch((err) => { + void transport.close().catch((err: unknown) => { this.opts.log( `[telegram][diag] failed to close transport (${context}): ${formatCloseError(err)}`, ); diff --git a/extensions/telegram/src/probe.ts b/extensions/telegram/src/probe.ts index 4fe994d7dfc..ad67e88e976 100644 --- a/extensions/telegram/src/probe.ts +++ b/extensions/telegram/src/probe.ts @@ -178,7 +178,10 @@ export async function probeTelegram( } if (!meRes) { - throw fetchError ?? new Error(`probe timed out after ${timeoutBudgetMs}ms`); + throw toLintErrorObject( + fetchError ?? new Error(`probe timed out after ${timeoutBudgetMs}ms`), + "Non-Error thrown", + ); } const meJson = (await meRes.json()) as { @@ -254,3 +257,17 @@ export async function probeTelegram( }; } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/telegram/src/send.proxy.test.ts b/extensions/telegram/src/send.proxy.test.ts index 7c85995d0ac..6c8d1197b3c 100644 --- a/extensions/telegram/src/send.proxy.test.ts +++ b/extensions/telegram/src/send.proxy.test.ts @@ -189,7 +189,11 @@ describe("telegram proxy client", () => { (_input: RequestInfo | URL, init?: RequestInit) => new Promise((_resolve, reject) => { const signal = init?.signal as AbortSignal; - signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + signal.addEventListener( + "abort", + () => reject(toLintErrorObject(signal.reason, "Non-Error rejection")), + { once: true }, + ); }), ); @@ -212,3 +216,17 @@ describe("telegram proxy client", () => { vi.useRealTimers(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/telegram/src/send.ts b/extensions/telegram/src/send.ts index cdc0aa3dfd7..563c11d3eed 100644 --- a/extensions/telegram/src/send.ts +++ b/extensions/telegram/src/send.ts @@ -527,7 +527,7 @@ function createTelegramRequestWithDiag(params: { fn: runRequest, ...(options?.shouldLog ? { shouldLog: options.shouldLog } : {}), }); - return call.catch((err) => { + return call.catch((err: unknown) => { logHttpError(label ?? "request", err); throw err; }); @@ -567,7 +567,7 @@ function createRequestWithChatNotFound(params: { input: string; }) { return async (fn: () => Promise, label: string) => - params.requestWithDiag(fn, label).catch((err) => { + params.requestWithDiag(fn, label).catch((err: unknown) => { throw wrapTelegramChatNotFoundError(err, { chatId: params.chatId, input: params.input, diff --git a/extensions/telegram/src/telegram-ingress-worker.runtime.ts b/extensions/telegram/src/telegram-ingress-worker.runtime.ts index 7b2e523e868..d7cdd7173dc 100644 --- a/extensions/telegram/src/telegram-ingress-worker.runtime.ts +++ b/extensions/telegram/src/telegram-ingress-worker.runtime.ts @@ -214,7 +214,7 @@ main() .then(() => { parentPort?.close(); }) - .catch((err) => { + .catch((err: unknown) => { post({ type: "poll-error", message: formatErrorMessage(err), finishedAt: Date.now() }); parentPort?.close(); process.exitCode = stopped ? 0 : 1; diff --git a/extensions/telegram/src/thread-bindings.ts b/extensions/telegram/src/thread-bindings.ts index ff87de38da0..c690fc821c8 100644 --- a/extensions/telegram/src/thread-bindings.ts +++ b/extensions/telegram/src/thread-bindings.ts @@ -462,7 +462,7 @@ function persistBindingsSafely(params: { bindings?: TelegramThreadBindingRecord[]; reason: string; }): void { - void enqueuePersistBindings(params).catch((err) => { + void enqueuePersistBindings(params).catch((err: unknown) => { logVerbose( `telegram thread bindings persist failed (${params.accountId}, ${params.reason}): ${String(err)}`, ); diff --git a/extensions/telegram/src/webhook.test.ts b/extensions/telegram/src/webhook.test.ts index 7039df8edbe..ac9b65d55bf 100644 --- a/extensions/telegram/src/webhook.test.ts +++ b/extensions/telegram/src/webhook.test.ts @@ -346,7 +346,7 @@ async function postWebhookPayloadWithChunkPlan(params: { req.end(); }; - void writeAll().catch((error) => { + void writeAll().catch((error: unknown) => { settle.reject(error); }); }); diff --git a/extensions/telegram/src/webhook.ts b/extensions/telegram/src/webhook.ts index 208b0a7f7cd..22b94632668 100644 --- a/extensions/telegram/src/webhook.ts +++ b/extensions/telegram/src/webhook.ts @@ -366,7 +366,7 @@ export async function startTelegramWebhook(opts: { durationMs: Date.now() - startTime, }); } - })().catch((err) => { + })().catch((err: unknown) => { const errMsg = formatErrorMessage(err); if (diagnosticsEnabled) { logWebhookError({ @@ -377,7 +377,7 @@ export async function startTelegramWebhook(opts: { } runtime.log?.(`webhook update processing failed after ack: ${errMsg}`); }); - })().catch((err) => { + })().catch((err: unknown) => { const errMsg = formatErrorMessage(err); if (diagnosticsEnabled) { logWebhookError({ diff --git a/extensions/tlon/src/monitor/index.ts b/extensions/tlon/src/monitor/index.ts index 28c19febcba..fffaa313182 100644 --- a/extensions/tlon/src/monitor/index.ts +++ b/extensions/tlon/src/monitor/index.ts @@ -462,8 +462,10 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise - runtime.error?.(`[tlon] Failed to send security warning to owner: ${err}`), + }).catch((err: unknown) => + runtime.error?.( + `[tlon] Failed to send security warning to owner: ${formatErrorMessage(err)}`, + ), ); } } diff --git a/extensions/tlon/src/urbit/sse-client.ts b/extensions/tlon/src/urbit/sse-client.ts index 9ee960f7263..bd4eb85b921 100644 --- a/extensions/tlon/src/urbit/sse-client.ts +++ b/extensions/tlon/src/urbit/sse-client.ts @@ -209,7 +209,7 @@ export class UrbitSSEClient { throw new Error(`Stream connection failed: ${response.status}`); } - this.processStream(response.body).catch((error) => { + this.processStream(response.body).catch((error: unknown) => { if (!this.aborted) { this.logger.error?.(`Stream error: ${String(error)}`); for (const { err } of this.eventHandlers.values()) { @@ -286,7 +286,7 @@ export class UrbitSSEClient { this.logger.log?.( `[SSE] Acking event ${eventId} (last acked: ${this.lastAcknowledgedEventId})`, ); - this.ack(eventId).catch((err) => { + this.ack(eventId).catch((err: unknown) => { this.logger.error?.(`Failed to ack event ${eventId}: ${String(err)}`); }); } diff --git a/extensions/twitch/src/monitor.ts b/extensions/twitch/src/monitor.ts index 0827f4e81cf..3d634d713bc 100644 --- a/extensions/twitch/src/monitor.ts +++ b/extensions/twitch/src/monitor.ts @@ -289,7 +289,7 @@ export async function monitorTwitchProvider( core, statusSink, }); - })().catch((err) => { + })().catch((err: unknown) => { runtime.error?.(`Message processing failed: ${String(err)}`); }); }); diff --git a/extensions/voice-call/index.ts b/extensions/voice-call/index.ts index 998ea3d078c..a63eb18d35f 100644 --- a/extensions/voice-call/index.ts +++ b/extensions/voice-call/index.ts @@ -833,7 +833,7 @@ export default definePluginEntry({ ); return; } - void ensureRuntime().catch((err) => { + void ensureRuntime().catch((err: unknown) => { api.logger.error(`[voice-call] Failed to start runtime: ${formatErrorMessage(err)}`); }); }, diff --git a/extensions/voice-call/src/gateway-continue-operation.ts b/extensions/voice-call/src/gateway-continue-operation.ts index a24f1ee94d0..a3e9e5acf19 100644 --- a/extensions/voice-call/src/gateway-continue-operation.ts +++ b/extensions/voice-call/src/gateway-continue-operation.ts @@ -135,7 +135,7 @@ export function createVoiceCallContinueOperationStore(params: { result: { success: true, transcript: result.transcript }, }); }) - .catch((err) => { + .catch((err: unknown) => { const current = operations.get(operationId); if (!current || current.status !== "pending") { return; diff --git a/extensions/voice-call/src/manager.ts b/extensions/voice-call/src/manager.ts index afe3e98638e..d332aee401b 100644 --- a/extensions/voice-call/src/manager.ts +++ b/extensions/voice-call/src/manager.ts @@ -204,7 +204,7 @@ export class CallManager { providerCallId: call.providerCallId, reason: "timeout", }) - .catch((err) => { + .catch((err: unknown) => { console.warn( `[voice-call] Failed to hang up expired restored call ${callId}:`, err instanceof Error ? err.message : String(err), @@ -401,7 +401,7 @@ export class CallManager { return; } - void this.speakInitialMessage(call.providerCallId).catch((err) => { + void this.speakInitialMessage(call.providerCallId).catch((err: unknown) => { console.warn( `[voice-call] Failed to speak initial message for call ${call.callId}: ${formatErrorMessage(err)}`, ); diff --git a/extensions/voice-call/src/manager/events.ts b/extensions/voice-call/src/manager/events.ts index 0de338a1793..30661ec6589 100644 --- a/extensions/voice-call/src/manager/events.ts +++ b/extensions/voice-call/src/manager/events.ts @@ -177,7 +177,7 @@ export function processEvent(ctx: EventContext, event: NormalizedEvent): void { providerCallId: pid, reason: "hangup-bot", }) - .catch((err) => { + .catch((err: unknown) => { ctx.rejectedProviderCallIds.delete(pid); const message = formatErrorMessage(err); console.warn(`[voice-call] Failed to reject inbound call ${pid}:`, message); @@ -244,7 +244,7 @@ export function processEvent(ctx: EventContext, event: NormalizedEvent): void { } : {}), }) - .catch((err) => { + .catch((err: unknown) => { const message = formatErrorMessage(err); console.warn( `[voice-call] Failed to answer inbound call ${call.providerCallId}:`, diff --git a/extensions/voice-call/src/manager/timers.test.ts b/extensions/voice-call/src/manager/timers.test.ts index 0f22e5be238..5a7d250a099 100644 --- a/extensions/voice-call/src/manager/timers.test.ts +++ b/extensions/voice-call/src/manager/timers.test.ts @@ -132,7 +132,9 @@ describe("voice-call manager timers", () => { rejectTranscriptWaiter(ctx as never, "call-2", "provider failed"); await expect(another).rejects.toThrow("provider failed"); - const timedOut = waitForFinalTranscript(ctx as never, "call-3").catch((error) => error); + const timedOut = waitForFinalTranscript(ctx as never, "call-3").catch( + (error: unknown) => error, + ); await vi.advanceTimersByTimeAsync(1_000); const timeoutError = await timedOut; expect(timeoutError).toBeInstanceOf(Error); diff --git a/extensions/voice-call/src/providers/twilio.ts b/extensions/voice-call/src/providers/twilio.ts index 070b80c3793..45990c9c1b9 100644 --- a/extensions/voice-call/src/providers/twilio.ts +++ b/extensions/voice-call/src/providers/twilio.ts @@ -447,7 +447,6 @@ export class TwilioProvider implements VoiceCallProvider { const streamUrl = view.callSid ? this.getStreamUrlForCall(view.callSid) : null; return streamUrl ? this.getStreamConnectXml(streamUrl) : TwilioProvider.PAUSE_TWIML; } - case "empty": default: return TwilioProvider.EMPTY_TWIML; } diff --git a/extensions/voice-call/src/webhook.ts b/extensions/voice-call/src/webhook.ts index 3707a5aab1b..2f165fa09b9 100644 --- a/extensions/voice-call/src/webhook.ts +++ b/extensions/voice-call/src/webhook.ts @@ -432,7 +432,7 @@ export class VoiceCallWebhookServer { const callMode = call.metadata?.mode as string | undefined; const shouldRespond = call.direction === "inbound" || callMode === "conversation"; if (shouldRespond) { - this.handleInboundResponse(call.callId, transcript).catch((err) => { + this.handleInboundResponse(call.callId, transcript).catch((err: unknown) => { console.warn(`[voice-call] Failed to auto-respond:`, err); }); } @@ -467,7 +467,7 @@ export class VoiceCallWebhookServer { } }, onTranscriptionReady: (callId) => { - this.manager.speakInitialMessage(callId).catch((err) => { + this.manager.speakInitialMessage(callId).catch((err: unknown) => { console.warn(`[voice-call] Failed to speak initial message:`, err); }); }, @@ -495,7 +495,7 @@ export class VoiceCallWebhookServer { console.log( `[voice-call] Auto-ending call ${disconnectedCall.callId} after stream disconnect grace`, ); - void this.manager.endCall(disconnectedCall.callId).catch((err) => { + void this.manager.endCall(disconnectedCall.callId).catch((err: unknown) => { console.warn(`[voice-call] Failed to auto-end call ${disconnectedCall.callId}:`, err); }); }, STREAM_DISCONNECT_HANGUP_GRACE_MS); @@ -533,7 +533,7 @@ export class VoiceCallWebhookServer { this.startPromise = new Promise((resolve, reject) => { this.server = http.createServer((req, res) => { - this.handleRequest(req, res, webhookPath).catch((err) => { + this.handleRequest(req, res, webhookPath).catch((err: unknown) => { console.error("[voice-call] Webhook error:", err); res.statusCode = 500; res.end("Internal Server Error"); @@ -860,7 +860,7 @@ export class VoiceCallWebhookServer { const response = buildResponse() .then(cloneWebhookResponsePayload) - .catch((err) => { + .catch((err: unknown) => { this.replayResponses.delete(key); throw err; }); @@ -958,7 +958,6 @@ export class VoiceCallWebhookServer { normalizePhoneNumber(params.get("From") ?? undefined), this.config.allowFrom, ); - case "disabled": default: return false; } diff --git a/extensions/voice-call/src/webhook/realtime-handler.ts b/extensions/voice-call/src/webhook/realtime-handler.ts index fbd32210a35..5bc24ed5e18 100644 --- a/extensions/voice-call/src/webhook/realtime-handler.ts +++ b/extensions/voice-call/src/webhook/realtime-handler.ts @@ -900,7 +900,7 @@ export class RealtimeCallHandler { closeSession(); }; - session.connect().catch((error: Error) => { + session.connect().catch((error: unknown) => { console.error("[voice-call] Failed to connect realtime bridge:", error); session.close(); emitCallEnd("error"); diff --git a/extensions/voice-call/src/webhook/stale-call-reaper.ts b/extensions/voice-call/src/webhook/stale-call-reaper.ts index 6283ffa03f2..41db68de1d7 100644 --- a/extensions/voice-call/src/webhook/stale-call-reaper.ts +++ b/extensions/voice-call/src/webhook/stale-call-reaper.ts @@ -25,7 +25,7 @@ export function startStaleCallReaper(params: { console.log( `[voice-call] Reaping stale call ${call.callId} (age: ${Math.round(age / 1000)}s, state: ${call.state})`, ); - void params.manager.endCall(call.callId).catch((err) => { + void params.manager.endCall(call.callId).catch((err: unknown) => { console.warn(`[voice-call] Reaper failed to end call ${call.callId}:`, err); }); } diff --git a/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts b/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts index 54a3acfcd9a..daa3c23abce 100644 --- a/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts +++ b/extensions/whatsapp/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts @@ -205,7 +205,7 @@ describe("web auto-reply connection", () => { }, }; const listenerFactory = vi.fn(async () => { - throw boom428; + throw toLintErrorObject(boom428, "Non-Error thrown"); }); const sleep = vi.fn(async () => {}); @@ -1142,3 +1142,17 @@ describe("web auto-reply connection", () => { expect(markDispatchIdle).toHaveBeenCalled(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/whatsapp/src/auto-reply/monitor.ts b/extensions/whatsapp/src/auto-reply/monitor.ts index cb2a3d45857..6bd1ead0653 100644 --- a/extensions/whatsapp/src/auto-reply/monitor.ts +++ b/extensions/whatsapp/src/auto-reply/monitor.ts @@ -545,7 +545,7 @@ export async function monitorWebChannel( normalizeReconnectAccountId(entry.accountId) === normalizedAccountId, bypassBackoff: isNoListenerReconnectError(entry.lastError), }), - }).catch((err) => { + }).catch((err: unknown) => { reconnectLogger.warn( { connectionId: connection.connectionId, error: String(err) }, "reconnect drain failed", @@ -564,7 +564,7 @@ export async function monitorWebChannel( normalizeReconnectAccountId(entry.accountId) === normalizedAccountId, bypassBackoff: false, }), - }).catch((err) => { + }).catch((err: unknown) => { reconnectLogger.warn( { connectionId: connection.connectionId, error: String(err) }, "periodic drain failed", diff --git a/extensions/whatsapp/src/auto-reply/monitor/last-route.ts b/extensions/whatsapp/src/auto-reply/monitor/last-route.ts index 69007c8ced8..95a42aa17a2 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/last-route.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/last-route.ts @@ -37,7 +37,7 @@ export function updateLastRouteInBackground(params: { accountId: params.accountId, }, ctx: params.ctx, - }).catch((err) => { + }).catch((err: unknown) => { params.warn( { error: formatError(err), diff --git a/extensions/whatsapp/src/connection-controller.ts b/extensions/whatsapp/src/connection-controller.ts index c02bdcef735..882700cb973 100644 --- a/extensions/whatsapp/src/connection-controller.ts +++ b/extensions/whatsapp/src/connection-controller.ts @@ -484,7 +484,7 @@ export class WhatsAppConnectionController { return "aborted"; } const listenerClose = - connection.listener.onClose?.catch((err) => ({ + connection.listener.onClose?.catch((err: unknown) => ({ status: 500, isLoggedOut: false, error: err, diff --git a/extensions/whatsapp/src/creds-persistence.ts b/extensions/whatsapp/src/creds-persistence.ts index 9ad82529022..9a4de79bb46 100644 --- a/extensions/whatsapp/src/creds-persistence.ts +++ b/extensions/whatsapp/src/creds-persistence.ts @@ -50,7 +50,7 @@ export function enqueueCredsSave( const previous = credsSaveQueues.get(authDir) ?? Promise.resolve(); const next = previous .then(() => saveCreds()) - .catch((error) => { + .catch((error: unknown) => { onError(error); }) .finally(() => { diff --git a/extensions/whatsapp/src/inbound/media.ts b/extensions/whatsapp/src/inbound/media.ts index 1ed6438af46..19f2af13bd0 100644 --- a/extensions/whatsapp/src/inbound/media.ts +++ b/extensions/whatsapp/src/inbound/media.ts @@ -84,7 +84,7 @@ export async function downloadInboundMedia( "inbound", maxBytes, fileName, - ).catch((err) => { + ).catch((err: unknown) => { if (err instanceof Error && /Media exceeds/i.test(err.message)) { throw new WhatsAppInboundMediaLimitExceededError(maxBytes); } diff --git a/extensions/whatsapp/src/inbound/monitor.ts b/extensions/whatsapp/src/inbound/monitor.ts index e5ed722c0d0..c662bea7cdc 100644 --- a/extensions/whatsapp/src/inbound/monitor.ts +++ b/extensions/whatsapp/src/inbound/monitor.ts @@ -1085,7 +1085,7 @@ export async function attachWebInboxToSocket( } try { const task = Promise.resolve(debouncer.enqueue(inboundMessage)); - void task.catch((err) => { + void task.catch((err: unknown) => { inboundLogger.error({ error: String(err) }, "failed handling inbound web message"); inboundConsoleLog.error(`Failed handling inbound web message: ${String(err)}`); }); @@ -1121,7 +1121,7 @@ export async function attachWebInboxToSocket( } }; const handleMessagesUpsertEvent = (upsert: { type?: string; messages?: Array }) => { - const task = handleMessagesUpsert(upsert).catch((err) => { + const task = handleMessagesUpsert(upsert).catch((err: unknown) => { inboundLogger.error({ error: String(err) }, "messages.upsert handler error"); inboundConsoleLog.error(`Messages upsert handler error: ${String(err)}`); }); @@ -1213,7 +1213,7 @@ export async function attachWebInboxToSocket( handleConnectionUpdate as unknown as (...args: unknown[]) => void, ); - const replayTask = replayPendingDurableInboundMessages().catch((err) => { + const replayTask = replayPendingDurableInboundMessages().catch((err: unknown) => { inboundLogger.error({ error: String(err) }, "failed replaying durable WhatsApp inbound"); inboundConsoleLog.error(`Failed replaying durable WhatsApp inbound: ${String(err)}`); }); diff --git a/extensions/whatsapp/src/login-qr.ts b/extensions/whatsapp/src/login-qr.ts index a5583179b18..7376cf7b29a 100644 --- a/extensions/whatsapp/src/login-qr.ts +++ b/extensions/whatsapp/src/login-qr.ts @@ -229,7 +229,7 @@ function attachLoginWaiter(accountId: string, login: ActiveLogin) { current.error = result.message; current.errorStatus = result.statusCode; }) - .catch((err) => { + .catch((err: unknown) => { const current = activeLogins.get(accountId); if (current?.id !== login.id) { return; @@ -246,7 +246,7 @@ async function waitForQrOrRecoveredLogin(params: { }): Promise { const qrResult = params.qrPromise.then( (qr) => ({ outcome: "qr", qr }) as const, - (err) => + (err: unknown) => ({ outcome: "failed", message: `Failed to get QR: ${String(err)}`, diff --git a/extensions/whatsapp/src/login.ts b/extensions/whatsapp/src/login.ts index f859f683d7a..1a81e2be992 100644 --- a/extensions/whatsapp/src/login.ts +++ b/extensions/whatsapp/src/login.ts @@ -26,7 +26,7 @@ export async function loginWeb( .then((output) => { runtime.log(output.endsWith("\n") ? output.slice(0, -1) : output); }) - .catch((err) => { + .catch((err: unknown) => { runtime.error(`failed rendering WhatsApp QR: ${String(err)}`); }); }; diff --git a/extensions/whatsapp/src/session.ts b/extensions/whatsapp/src/session.ts index fb141dffea6..d60ea11fd4b 100644 --- a/extensions/whatsapp/src/session.ts +++ b/extensions/whatsapp/src/session.ts @@ -196,7 +196,7 @@ export async function createWaSocket( opts.onQr?.(qr); if (printQr) { console.log("Open the WhatsApp app, go to Linked Devices, then scan this QR:"); - void printTerminalQr(qr).catch((err) => { + void printTerminalQr(qr).catch((err: unknown) => { sessionLogger.warn({ error: String(err) }, "failed rendering WhatsApp QR"); }); } @@ -326,7 +326,12 @@ export async function waitForWaConnection(sock: ReturnType) } if (update.connection === "close") { evWithOff.off?.("connection.update", handler); - reject(update.lastDisconnect ?? new Error("Connection closed")); + reject( + toLintErrorObject( + update.lastDisconnect ?? new Error("Connection closed"), + "Non-Error rejection", + ), + ); } }; @@ -337,3 +342,17 @@ export async function waitForWaConnection(sock: ReturnType) export function newConnectionId() { return randomUUID(); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/extensions/xai/video-generation-provider.ts b/extensions/xai/video-generation-provider.ts index b6bbb499e61..6d5c88bb37f 100644 --- a/extensions/xai/video-generation-provider.ts +++ b/extensions/xai/video-generation-provider.ts @@ -296,8 +296,6 @@ function resolveCreateEndpoint(req: VideoGenerationRequest): string { return "/videos/edits"; case "extend": return "/videos/extensions"; - case "referenceToVideo": - case "generate": default: return "/videos/generations"; } diff --git a/extensions/zalo/src/monitor.webhook.ts b/extensions/zalo/src/monitor.webhook.ts index 877a7b2b36f..5a142cc5f63 100644 --- a/extensions/zalo/src/monitor.webhook.ts +++ b/extensions/zalo/src/monitor.webhook.ts @@ -266,7 +266,7 @@ export async function handleZaloWebhookRequest( update, processUpdate, nowMs, - }).catch((err) => { + }).catch((err: unknown) => { target.runtime.error?.(`[${target.account.accountId}] Zalo webhook failed: ${String(err)}`); }); diff --git a/extensions/zalouser/src/monitor.ts b/extensions/zalouser/src/monitor.ts index c33efde1973..4638cd55481 100644 --- a/extensions/zalouser/src/monitor.ts +++ b/extensions/zalouser/src/monitor.ts @@ -294,7 +294,7 @@ async function processMessage( const configuredGroupName = message.groupName?.trim() || ""; const groupContext = isGroup && !configuredGroupName - ? await resolveZaloGroupContext(account.profile, chatId).catch((err) => { + ? await resolveZaloGroupContext(account.profile, chatId).catch((err: unknown) => { logVerbose( core, runtime, @@ -986,7 +986,7 @@ export async function monitorZalouserProvider( statusSink, ); }) - .catch((err) => { + .catch((err: unknown) => { runtime.error(`[${account.accountId}] Failed to process message: ${String(err)}`); }); }, diff --git a/packages/agent-core/src/agent-loop.ts b/packages/agent-core/src/agent-loop.ts index bb680831687..05e8177dedc 100644 --- a/packages/agent-core/src/agent-loop.ts +++ b/packages/agent-core/src/agent-loop.ts @@ -63,7 +63,7 @@ export function agentLoop( .then((messages) => { stream.end(messages); }) - .catch((error) => { + .catch((error: unknown) => { pushLoopFailure(stream, config, error, signal?.aborted === true); }); @@ -108,7 +108,7 @@ export function agentLoopContinue( .then((messages) => { stream.end(messages); }) - .catch((error) => { + .catch((error: unknown) => { pushLoopFailure(stream, config, error, signal?.aborted === true); }); diff --git a/packages/agent-core/src/harness/agent-harness.ts b/packages/agent-core/src/harness/agent-harness.ts index 9a9684e8cd3..a8251a5f439 100644 --- a/packages/agent-core/src/harness/agent-harness.ts +++ b/packages/agent-core/src/harness/agent-harness.ts @@ -588,7 +588,7 @@ export class AgentHarness< const hadPendingMutations = this.pendingSessionWrites.length > 0; await this.flushPendingSessionWrites(); if (eventError) { - throw eventError; + throw toLintErrorObject(eventError, "Non-Error thrown"); } await this.emitOwn({ type: "save_point", hadPendingMutations }); return; @@ -1187,3 +1187,17 @@ export class AgentHarness< return () => handlers.delete(handler as AgentHarnessHandler); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/packages/agent-core/src/harness/types.ts b/packages/agent-core/src/harness/types.ts index b81e591d215..929f940e1b5 100644 --- a/packages/agent-core/src/harness/types.ts +++ b/packages/agent-core/src/harness/types.ts @@ -26,7 +26,7 @@ export function err(error: TError): Result { /** Return the success value or throw the failure error. Intended for tests and explicit adapter boundaries. */ export function getOrThrow(result: Result): TValue { if (!result.ok) { - throw result.error; + throw toLintErrorObject(result.error, "Non-Error thrown"); } return result.value; } @@ -892,3 +892,17 @@ export interface AgentHarnessOptions< } export type { AgentHarness } from "./agent-harness.js"; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/packages/gateway-client/src/client.ts b/packages/gateway-client/src/client.ts index c247227b8e3..79729224b54 100644 --- a/packages/gateway-client/src/client.ts +++ b/packages/gateway-client/src/client.ts @@ -878,7 +878,7 @@ export class GatewayClient { this.startTickWatch(); this.opts.onHelloOk?.(helloOk); }) - .catch((err) => { + .catch((err: unknown) => { this.pendingConnectErrorDetailCode = err instanceof GatewayClientRequestError ? readConnectErrorDetailCode(err.details) : null; this.pendingConnectErrorDetails = diff --git a/packages/media-core/src/read-response-with-limit.ts b/packages/media-core/src/read-response-with-limit.ts index 604ae6c30c8..30b4a241a94 100644 --- a/packages/media-core/src/read-response-with-limit.ts +++ b/packages/media-core/src/read-response-with-limit.ts @@ -34,10 +34,10 @@ async function readChunkWithIdleTimeout( resolve(result); } }, - (err) => { + (err: unknown) => { clear(); if (!timedOut) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } }, ); @@ -179,3 +179,17 @@ export async function readResponseTextSnippet( } return prefix.truncated ? `${collapsed}…` : collapsed; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/packages/memory-host-sdk/src/host/embeddings-worker.ts b/packages/memory-host-sdk/src/host/embeddings-worker.ts index 0defd7617e4..ec6a91bc525 100644 --- a/packages/memory-host-sdk/src/host/embeddings-worker.ts +++ b/packages/memory-host-sdk/src/host/embeddings-worker.ts @@ -252,7 +252,12 @@ class LocalEmbeddingWorkerClient { const abort = () => { this.pending.delete(id); this.shutdownChild(); - reject(options.signal?.reason ?? new Error("Local embedding request aborted")); + reject( + toLintErrorObject( + options.signal?.reason ?? new Error("Local embedding request aborted"), + "Non-Error rejection", + ), + ); }; options.signal.addEventListener("abort", abort, { once: true }); pending.abort = () => options.signal?.removeEventListener("abort", abort); @@ -362,3 +367,17 @@ export async function createLocalEmbeddingWorkerProvider( }, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/packages/memory-host-sdk/src/host/embeddings.test.ts b/packages/memory-host-sdk/src/host/embeddings.test.ts index da3be944e27..8807aee8520 100644 --- a/packages/memory-host-sdk/src/host/embeddings.test.ts +++ b/packages/memory-host-sdk/src/host/embeddings.test.ts @@ -414,7 +414,7 @@ process.on("message", (message) => { const embedPromise = provider.embedQuery("stuck"); const embedError = embedPromise.then( () => undefined, - (err) => err, + (err: unknown) => err, ); await expect .poll(async () => { diff --git a/packages/memory-host-sdk/src/host/embeddings.ts b/packages/memory-host-sdk/src/host/embeddings.ts index dd151e03957..9d31d420a05 100644 --- a/packages/memory-host-sdk/src/host/embeddings.ts +++ b/packages/memory-host-sdk/src/host/embeddings.ts @@ -41,7 +41,7 @@ async function disposeResources( } } if (firstError) { - throw firstError; + throw toLintErrorObject(firstError, "Non-Error thrown"); } } @@ -181,3 +181,17 @@ export async function createLocalEmbeddingProviderInProcess( }, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/packages/memory-host-sdk/src/host/retry-utils.ts b/packages/memory-host-sdk/src/host/retry-utils.ts index 4e91a9931f5..276b7a60fb0 100644 --- a/packages/memory-host-sdk/src/host/retry-utils.ts +++ b/packages/memory-host-sdk/src/host/retry-utils.ts @@ -106,7 +106,7 @@ export async function retryAsync( await sleep(resolveSafeTimeoutDelayMs(initialDelayMs * 2 ** i, { minMs: 0 })); } } - throw lastErr ?? new Error("Retry failed"); + throw toLintErrorObject(lastErr ?? new Error("Retry failed"), "Non-Error thrown"); } const options = attemptsOrOptions; @@ -151,5 +151,19 @@ export async function retryAsync( } } - throw lastErr ?? new Error("Retry failed"); + throw toLintErrorObject(lastErr ?? new Error("Retry failed"), "Non-Error thrown"); +} + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; } diff --git a/scripts/anthropic-prompt-probe.ts b/scripts/anthropic-prompt-probe.ts index 13afd077b58..75f3dbcd946 100644 --- a/scripts/anthropic-prompt-probe.ts +++ b/scripts/anthropic-prompt-probe.ts @@ -713,7 +713,7 @@ export const testing = { }; if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - await main().catch((error) => { + await main().catch((error: unknown) => { console.error(redactForDevToolLog(error instanceof Error ? error.message : String(error))); process.exitCode = 1; }); diff --git a/scripts/bench-gateway-restart.ts b/scripts/bench-gateway-restart.ts index 317114483f3..690e57d0080 100644 --- a/scripts/bench-gateway-restart.ts +++ b/scripts/bench-gateway-restart.ts @@ -1679,7 +1679,7 @@ export const testing = { }; if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - main().catch((err) => { + main().catch((err: unknown) => { console.error(err instanceof Error ? err.stack : String(err)); process.exitCode = 1; }); diff --git a/scripts/bench-gateway-startup.ts b/scripts/bench-gateway-startup.ts index acf6f3fa315..2b9b6527e6c 100644 --- a/scripts/bench-gateway-startup.ts +++ b/scripts/bench-gateway-startup.ts @@ -1095,7 +1095,7 @@ export const testing = { }; if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - main().catch((err) => { + main().catch((err: unknown) => { console.error(err instanceof Error ? err.stack : String(err)); process.exitCode = 1; }); diff --git a/scripts/check-changelog-attributions.mjs b/scripts/check-changelog-attributions.mjs index 3f2dfb44c0c..00e6c676027 100644 --- a/scripts/check-changelog-attributions.mjs +++ b/scripts/check-changelog-attributions.mjs @@ -119,8 +119,10 @@ export async function main(argv = process.argv.slice(2)) { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - main().catch((error) => { - console.error(error); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error); + process.exit(1); + }, + ); } diff --git a/scripts/check-dependency-pins.mjs b/scripts/check-dependency-pins.mjs index 1a874fac831..daf7c1ed311 100644 --- a/scripts/check-dependency-pins.mjs +++ b/scripts/check-dependency-pins.mjs @@ -154,8 +154,10 @@ export async function main() { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - main().catch((error) => { - console.error(error); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error); + process.exit(1); + }, + ); } diff --git a/scripts/check-docs-mdx.mjs b/scripts/check-docs-mdx.mjs index 90a391378dd..532c6acc2f2 100644 --- a/scripts/check-docs-mdx.mjs +++ b/scripts/check-docs-mdx.mjs @@ -346,7 +346,9 @@ async function main() { process.exitCode = 1; } -main().catch((error) => { - console.error(error?.stack ?? error); - process.exit(1); -}); +main().catch( + /** @param {unknown} error */ (error) => { + console.error(error?.stack ?? error); + process.exit(1); + }, +); diff --git a/scripts/check-extension-package-tsc-boundary.mjs b/scripts/check-extension-package-tsc-boundary.mjs index e5f10677bf1..47faf30c91a 100644 --- a/scripts/check-extension-package-tsc-boundary.mjs +++ b/scripts/check-extension-package-tsc-boundary.mjs @@ -400,7 +400,7 @@ export function runNodeStepAsync(label, args, timeoutMs, params = {}) { ); onFailure?.(error); abortSiblingSteps(abortController); - rejectPromise(error); + rejectPromise(toLintErrorObject(error, "Step timed out")); }, timeoutMs); child.stdout.setEncoding("utf8"); @@ -419,11 +419,14 @@ export function runNodeStepAsync(label, args, timeoutMs, params = {}) { settled = true; if (error.name === "AbortError" && abortController?.signal.aborted) { rejectPromise( - attachStepFailureMetadata(new Error(`${label} canceled after sibling failure`), label, { - kind: "canceled", - elapsedMs: Date.now() - startedAt, - note: "canceled after sibling failure", - }), + toLintErrorObject( + attachStepFailureMetadata(new Error(`${label} canceled after sibling failure`), label, { + kind: "canceled", + elapsedMs: Date.now() - startedAt, + note: "canceled after sibling failure", + }), + "Step canceled after sibling failure", + ), ); return; } @@ -450,7 +453,7 @@ export function runNodeStepAsync(label, args, timeoutMs, params = {}) { ); onFailure?.(failure); abortSiblingSteps(abortController); - rejectPromise(failure); + rejectPromise(toLintErrorObject(failure, "Step spawn failed")); }); child.on("close", (code) => { if (settled) { @@ -487,7 +490,7 @@ export function runNodeStepAsync(label, args, timeoutMs, params = {}) { ); onFailure?.(error); abortSiblingSteps(abortController); - rejectPromise(error); + rejectPromise(toLintErrorObject(error, "Step failed")); }); }); } @@ -519,7 +522,7 @@ export async function runNodeStepsWithConcurrency(steps, concurrency) { }); await Promise.allSettled(workers); if (firstFailure) { - throw firstFailure; + throw toLintErrorObject(firstFailure, "Non-Error thrown"); } } @@ -852,3 +855,17 @@ export async function main(argv = process.argv.slice(2)) { if (import.meta.main) { await main(); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/check-gateway-cpu-scenarios.mjs b/scripts/check-gateway-cpu-scenarios.mjs index d6b88efda7a..298cc9b045f 100644 --- a/scripts/check-gateway-cpu-scenarios.mjs +++ b/scripts/check-gateway-cpu-scenarios.mjs @@ -316,8 +316,10 @@ export const testing = { }; if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - main().catch((error) => { - console.error(error instanceof Error ? error.stack : String(error)); - process.exitCode = 1; - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exitCode = 1; + }, + ); } diff --git a/scripts/check-memory-fd-repro.mjs b/scripts/check-memory-fd-repro.mjs index 3202f6df21f..04c991ae115 100644 --- a/scripts/check-memory-fd-repro.mjs +++ b/scripts/check-memory-fd-repro.mjs @@ -761,10 +761,12 @@ function isMainModule() { } if (isMainModule()) { - main().catch((error) => { - console.error( - `[memory-fd-repro] failed: ${error instanceof Error ? error.message : String(error)}`, - ); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error( + `[memory-fd-repro] failed: ${error instanceof Error ? error.message : String(error)}`, + ); + process.exit(1); + }, + ); } diff --git a/scripts/check-no-conflict-markers.mjs b/scripts/check-no-conflict-markers.mjs index 9b37d5e8e5e..ef9c8a00ebc 100644 --- a/scripts/check-no-conflict-markers.mjs +++ b/scripts/check-no-conflict-markers.mjs @@ -64,10 +64,7 @@ export function findConflictMarkersInFiles(filePaths, readFile = fs.readFileSync return violations; } -export function listTrackedFilesWithConflictMarkerCandidates( - cwd = process.cwd(), - run = spawnSync, -) { +export function listTrackedFilesWithConflictMarkerCandidates(cwd = process.cwd(), run = spawnSync) { const result = run( "git", ["grep", "-l", "-z", "-I", "-E", CONFLICT_MARKER_GREP_PATTERN, "--", "."], @@ -110,8 +107,10 @@ export async function main() { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - main().catch((error) => { - console.error(error); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error); + process.exit(1); + }, + ); } diff --git a/scripts/check-package-patches.mjs b/scripts/check-package-patches.mjs index a9987766914..e091c28fe6a 100644 --- a/scripts/check-package-patches.mjs +++ b/scripts/check-package-patches.mjs @@ -138,8 +138,10 @@ export async function main() { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - main().catch((error) => { - console.error(error); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error); + process.exit(1); + }, + ); } diff --git a/scripts/check-plugin-gateway-gauntlet.mjs b/scripts/check-plugin-gateway-gauntlet.mjs index 42406d91449..d72a7fe5c12 100644 --- a/scripts/check-plugin-gateway-gauntlet.mjs +++ b/scripts/check-plugin-gateway-gauntlet.mjs @@ -6,12 +6,12 @@ import os from "node:os"; import path from "node:path"; import process from "node:process"; import { fileURLToPath } from "node:url"; +import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs"; import { parseNonNegativeInt, parsePositiveInt, parsePositiveNumber, } from "./lib/numeric-options.mjs"; -import { stripLeadingPackageManagerSeparator } from "./lib/arg-utils.mjs"; import { buildGauntletPrebuildEnv, collectGatewayCpuObservations, @@ -1005,8 +1005,10 @@ async function main() { } if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) { - main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exitCode = 1; - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; + }, + ); } diff --git a/scripts/check-plugin-sdk-subpath-exports.mjs b/scripts/check-plugin-sdk-subpath-exports.mjs index 061e37e9dfd..a03a3a2d471 100644 --- a/scripts/check-plugin-sdk-subpath-exports.mjs +++ b/scripts/check-plugin-sdk-subpath-exports.mjs @@ -134,7 +134,9 @@ async function main() { process.exit(1); } -main().catch((error) => { - console.error(error); - process.exit(1); -}); +main().catch( + /** @param {unknown} error */ (error) => { + console.error(error); + process.exit(1); + }, +); diff --git a/scripts/control-ui-i18n.ts b/scripts/control-ui-i18n.ts index f1d3cbf66aa..6850f6c7060 100644 --- a/scripts/control-ui-i18n.ts +++ b/scripts/control-ui-i18n.ts @@ -1192,10 +1192,10 @@ class TranslationClient { clearInterval(heartbeat); resolve(extractTranslationResult(assistantMessage)); }) - .catch((error) => { + .catch((error: unknown) => { clearTimeout(timer); clearInterval(heartbeat); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }); }); }); @@ -1660,8 +1660,22 @@ function isCliEntrypoint() { } if (isCliEntrypoint()) { - await main().catch((error) => { + await main().catch((error: unknown) => { console.error(formatErrorMessage(error)); process.exit(1); }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/debug-claude-usage.ts b/scripts/debug-claude-usage.ts index 3d4d0e9204b..39cfc6eb3f7 100644 --- a/scripts/debug-claude-usage.ts +++ b/scripts/debug-claude-usage.ts @@ -489,7 +489,7 @@ export const testing = { }; if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - await main().catch((error) => { + await main().catch((error: unknown) => { console.error( previewForDevToolLog(error instanceof Error ? error.message : String(error), 800), ); diff --git a/scripts/dependency-changes-report.mjs b/scripts/dependency-changes-report.mjs index 23b11f9c14a..d27f856dbd1 100644 --- a/scripts/dependency-changes-report.mjs +++ b/scripts/dependency-changes-report.mjs @@ -299,7 +299,7 @@ export async function main(argv = process.argv.slice(2)) { await writeArtifact(options.jsonPath, `${JSON.stringify(report, null, 2)}\n`); await writeArtifact(options.markdownPath, renderMarkdownReport(report)); const artifactHint = - typeof options.markdownPath === "string" ? " See " + options.markdownPath + "." : ""; + typeof options.markdownPath === "string" ? " See ".concat(options.markdownPath, ".") : ""; process.stdout.write( `INFO dependency change report: ${report.summary.addedPackages} added, ` + `${report.summary.removedPackages} removed, ${report.summary.changedPackages} changed ` + @@ -314,7 +314,7 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.met (exitCode) => { process.exitCode = exitCode; }, - (error) => { + /** @param {unknown} error */ (error) => { process.stderr.write(`${error.stack ?? error.message ?? String(error)}\n`); process.exitCode = 1; }, diff --git a/scripts/dependency-ownership-surface-report.mjs b/scripts/dependency-ownership-surface-report.mjs index c7b0260976d..70aa7f09130 100644 --- a/scripts/dependency-ownership-surface-report.mjs +++ b/scripts/dependency-ownership-surface-report.mjs @@ -449,7 +449,7 @@ function main(argv = process.argv.slice(2)) { } if (options.asJson) { const artifactHint = - typeof options.markdownPath === "string" ? " See " + options.markdownPath + "." : ""; + typeof options.markdownPath === "string" ? " See ".concat(options.markdownPath, ".") : ""; process.stdout.write( `INFO dependency ownership/install surface report: ` + `${report.summary.importerCount} workspace package entries, ` + diff --git a/scripts/dependency-vulnerability-gate.mjs b/scripts/dependency-vulnerability-gate.mjs index 2c4dd8dd54a..f3eac8f1db7 100644 --- a/scripts/dependency-vulnerability-gate.mjs +++ b/scripts/dependency-vulnerability-gate.mjs @@ -292,7 +292,7 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.met (exitCode) => { process.exitCode = exitCode; }, - (error) => { + /** @param {unknown} error */ (error) => { process.stderr.write(`${error.stack ?? error.message ?? String(error)}\n`); process.exitCode = 1; }, diff --git a/scripts/dev/discord-acp-plain-language-smoke.ts b/scripts/dev/discord-acp-plain-language-smoke.ts index 64d50d95c7c..bbc2aa594d3 100644 --- a/scripts/dev/discord-acp-plain-language-smoke.ts +++ b/scripts/dev/discord-acp-plain-language-smoke.ts @@ -534,7 +534,7 @@ async function requestDiscordJson(params: { label: `${params.errorPrefix} ${params.method} ${redactDiscordApiPath(params.path)}`, signal: controller.signal, maxBytes: responseBodyMaxBytes, - }).catch((error) => { + }).catch((error: unknown) => { if (isTooLargeError(error)) { throw error; } @@ -1039,7 +1039,7 @@ async function main(): Promise { return 0; } const result = await run().catch( - (err): FailureResult => ({ + (err: unknown): FailureResult => ({ ok: false, stage: "unexpected", smokeId: "n/a", diff --git a/scripts/dev/ios-node-e2e.ts b/scripts/dev/ios-node-e2e.ts index 16c3d3b2529..bf1dc7b4b9f 100644 --- a/scripts/dev/ios-node-e2e.ts +++ b/scripts/dev/ios-node-e2e.ts @@ -230,7 +230,7 @@ async function main() { idempotencyKey: randomUUID(), }, (t.timeoutMs ?? 12_000) + 2_000, - ).catch((err) => { + ).catch((err: unknown) => { results.push({ id: t.id, ok: false, error: formatErr(err) }); return null; }); diff --git a/scripts/dev/realtime-talk-live-smoke.ts b/scripts/dev/realtime-talk-live-smoke.ts index 4603cc8e70d..f518a0dcc10 100644 --- a/scripts/dev/realtime-talk-live-smoke.ts +++ b/scripts/dev/realtime-talk-live-smoke.ts @@ -474,9 +474,9 @@ async function smokeGoogleLiveBrowserWs(browser: Browser, apiKey: string): Promi } window.clearTimeout(timeout); resolve({ setupComplete: true, readyState: ws.readyState }); - })().catch((error) => { + })().catch((error: unknown) => { window.clearTimeout(timeout); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }); }); ws.addEventListener("error", () => { @@ -751,7 +751,7 @@ async function main(): Promise { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - await main().catch((error) => { + await main().catch((error: unknown) => { console.error(shortError(error)); process.exitCode = 1; }); @@ -763,3 +763,17 @@ export const testing = { readBoundedText, resolveOpenAIHttpTimeoutMs, }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/dev/tui-pty-test-watch.ts b/scripts/dev/tui-pty-test-watch.ts index 7bf4f37bcbe..a53e8a3e5d2 100644 --- a/scripts/dev/tui-pty-test-watch.ts +++ b/scripts/dev/tui-pty-test-watch.ts @@ -288,7 +288,7 @@ async function main(): Promise { } } -main().catch((error) => { +main().catch((error: unknown) => { process.stderr.write( `${error instanceof Error ? error.stack || error.message : String(error)}\n`, ); diff --git a/scripts/e2e/crestodian-first-run-docker-client.ts b/scripts/e2e/crestodian-first-run-docker-client.ts index b8a7ee9d45c..f6e62121f62 100644 --- a/scripts/e2e/crestodian-first-run-docker-client.ts +++ b/scripts/e2e/crestodian-first-run-docker-client.ts @@ -177,7 +177,7 @@ async function main() { console.log("Crestodian first-run Docker E2E passed"); } -main().catch((err) => { +main().catch((err: unknown) => { console.error(err); process.exit(1); }); diff --git a/scripts/e2e/crestodian-planner-docker-client.mjs b/scripts/e2e/crestodian-planner-docker-client.mjs index ef47f45ac9a..e6e53458e40 100644 --- a/scripts/e2e/crestodian-planner-docker-client.mjs +++ b/scripts/e2e/crestodian-planner-docker-client.mjs @@ -125,7 +125,9 @@ async function main() { process.exit(0); } -main().catch((err) => { - console.error(err); - process.exit(1); -}); +main().catch( + /** @param {unknown} err */ (err) => { + console.error(err); + process.exit(1); + }, +); diff --git a/scripts/e2e/crestodian-rescue-docker-client.ts b/scripts/e2e/crestodian-rescue-docker-client.ts index 11e9ae5d713..4c845e4019e 100644 --- a/scripts/e2e/crestodian-rescue-docker-client.ts +++ b/scripts/e2e/crestodian-rescue-docker-client.ts @@ -264,7 +264,7 @@ async function main() { console.log("Crestodian rescue Docker E2E passed"); } -main().catch((err) => { +main().catch((err: unknown) => { console.error(err); process.exit(1); }); diff --git a/scripts/e2e/kitchen-sink-rpc-walk.mjs b/scripts/e2e/kitchen-sink-rpc-walk.mjs index fe6e55a05d9..a620ba3a916 100644 --- a/scripts/e2e/kitchen-sink-rpc-walk.mjs +++ b/scripts/e2e/kitchen-sink-rpc-walk.mjs @@ -504,7 +504,10 @@ async function retryRpcCall(method, params, options) { await delay(500); } } - throw lastError ?? new Error(`gateway RPC ${method} timed out before retry`); + throw toLintErrorObject( + lastError ?? new Error(`gateway RPC ${method} timed out before retry`), + "Non-Error thrown", + ); } function isRetryableGatewayCallError(error) { @@ -585,7 +588,7 @@ export async function fetchJson(url, options = {}) { } } } - throw lastError ?? new Error(`fetch ${url} failed`); + throw toLintErrorObject(lastError ?? new Error(`fetch ${url} failed`), "Non-Error thrown"); } export async function readBoundedResponseText(response, byteLimit = FETCH_BODY_MAX_BYTES) { @@ -1630,3 +1633,17 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me await main(); } } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs index 8055a315757..ef606542000 100644 --- a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs +++ b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs @@ -500,7 +500,10 @@ async function assertHttpOk(port, pathName) { } await delay(500); } - throw lastError ?? new Error(`${pathName} did not return HTTP 200`); + throw toLintErrorObject( + lastError ?? new Error(`${pathName} did not return HTTP 200`), + "Non-Error thrown", + ); } async function assertReadyzProbe(options) { @@ -524,7 +527,10 @@ async function assertReadyzProbe(options) { } await delay(500); } - throw lastError ?? new Error("/readyz did not return HTTP 200"); + throw toLintErrorObject( + lastError ?? new Error("/readyz did not return HTTP 200"), + "Non-Error thrown", + ); } async function rpcCall(method, params, options) { @@ -569,7 +575,10 @@ async function retryRpcCall(method, params, options) { await delay(500); } } - throw lastError ?? new Error(`gateway RPC ${method} timed out before retry`); + throw toLintErrorObject( + lastError ?? new Error(`gateway RPC ${method} timed out before retry`), + "Non-Error thrown", + ); } function isRetryableGatewayCallError(error) { @@ -1036,3 +1045,17 @@ export async function main(argv = process.argv.slice(2)) { if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { await main(); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/e2e/lib/clawhub-fixture-server.cjs b/scripts/e2e/lib/clawhub-fixture-server.cjs index 957e9544f94..ca8d5df9ba1 100644 --- a/scripts/e2e/lib/clawhub-fixture-server.cjs +++ b/scripts/e2e/lib/clawhub-fixture-server.cjs @@ -467,7 +467,9 @@ async function main() { }); } -main().catch((error) => { - console.error(error); - process.exit(1); -}); +main().catch( + /** @param {unknown} error */ (error) => { + console.error(error); + process.exit(1); + }, +); diff --git a/scripts/e2e/lib/codex-media-path/client.mjs b/scripts/e2e/lib/codex-media-path/client.mjs index 633ea153ee9..4bec122b93f 100644 --- a/scripts/e2e/lib/codex-media-path/client.mjs +++ b/scripts/e2e/lib/codex-media-path/client.mjs @@ -114,7 +114,7 @@ async function connectGateway() { }, reject: (error) => { clearTimeout(timer); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, }); ws.send(JSON.stringify({ type: "req", id, method, params: params ?? {} })); @@ -236,3 +236,17 @@ try { } finally { await gateway.close(); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/e2e/lib/openai-web-search-minimal/client.mjs b/scripts/e2e/lib/openai-web-search-minimal/client.mjs index 8a6f180ff8d..5455e1ce0b3 100644 --- a/scripts/e2e/lib/openai-web-search-minimal/client.mjs +++ b/scripts/e2e/lib/openai-web-search-minimal/client.mjs @@ -65,8 +65,22 @@ if (mode === "reject") { process.exit(0); } if (!result.ok) { - throw result.error; + throw toLintErrorObject(result.error, "Non-Error thrown"); } if (result.value?.status !== "ok") { throw new Error(`agent run did not complete successfully: ${JSON.stringify(result.value)}`); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/e2e/mcp-channels-docker-client.ts b/scripts/e2e/mcp-channels-docker-client.ts index ac5a3b1cf7e..4831ba678b2 100644 --- a/scripts/e2e/mcp-channels-docker-client.ts +++ b/scripts/e2e/mcp-channels-docker-client.ts @@ -141,7 +141,7 @@ async function main() { ); }, 240_000, - ).catch((error) => { + ).catch((error: unknown) => { throw new Error( `timeout waiting for seeded MCP conversation: ${JSON.stringify( lastMcpConversationList, @@ -182,7 +182,7 @@ async function main() { return currentMessages.length >= 2 ? currentMessages : undefined; }, 240_000, - ).catch((error) => { + ).catch((error: unknown) => { throw new Error( `timeout waiting for seeded transcript messages: ${JSON.stringify(lastHistory, null, 2)}`, { cause: error }, diff --git a/scripts/e2e/npm-telegram-live-runner.ts b/scripts/e2e/npm-telegram-live-runner.ts index cd6475089cb..ab38eb8b7ea 100644 --- a/scripts/e2e/npm-telegram-live-runner.ts +++ b/scripts/e2e/npm-telegram-live-runner.ts @@ -98,7 +98,7 @@ async function formatRunnerErrorMessage(error: unknown) { } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { - main().catch(async (error) => { + main().catch(async (error: unknown) => { process.stderr.write( `package telegram live e2e failed: ${await formatRunnerErrorMessage(error)}\n`, ); diff --git a/scripts/e2e/openwebui-probe.mjs b/scripts/e2e/openwebui-probe.mjs index 4bdba3abb6b..56d647d1a14 100644 --- a/scripts/e2e/openwebui-probe.mjs +++ b/scripts/e2e/openwebui-probe.mjs @@ -220,10 +220,12 @@ let modelIds = []; let targetModel = ""; let lastModelsError = ""; for (let attempt = 1; attempt <= modelAttempts; attempt += 1) { - const modelsResult = await fetchModels(authHeaders, attempt).catch((error) => { - lastModelsError = error instanceof Error ? error.message : String(error); - return undefined; - }); + const modelsResult = await fetchModels(authHeaders, attempt).catch( + /** @param {unknown} error */ (error) => { + lastModelsError = error instanceof Error ? error.message : String(error); + return undefined; + }, + ); if (modelsResult?.ok) { modelIds = extractModelIds(modelsResult.json); targetModel = diff --git a/scripts/e2e/secret-provider-integrations.mjs b/scripts/e2e/secret-provider-integrations.mjs index f7dfeea287d..391dcfcfe99 100644 --- a/scripts/e2e/secret-provider-integrations.mjs +++ b/scripts/e2e/secret-provider-integrations.mjs @@ -1103,10 +1103,10 @@ async function p8ManagedServiceEnvProof() { } } if (proofError) { - throw proofError; + throw toLintErrorObject(proofError, "Non-Error thrown"); } if (cleanupError) { - throw cleanupError; + throw toLintErrorObject(cleanupError, "Non-Error thrown"); } }); return "real managed service install preserved auth-profile exec provider passEnv"; @@ -1578,7 +1578,7 @@ async function main() { }); } if (runError) { - throw runError; + throw toLintErrorObject(runError, "Non-Error thrown"); } const failed = results.filter((entry) => entry.status !== "pass"); if (failed.length > 0) { @@ -1591,3 +1591,17 @@ export { gatewayCall, runCommand, startGateway, waitForManagedGatewayStatus }; if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) { await main(); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/embedded-run-abort-leak.ts b/scripts/embedded-run-abort-leak.ts index 987ec5b31a2..edae883f576 100644 --- a/scripts/embedded-run-abort-leak.ts +++ b/scripts/embedded-run-abort-leak.ts @@ -193,9 +193,9 @@ function abortableExtracted(signal: AbortSignal, promise: Promise): Promis signal.removeEventListener("abort", onAbort); resolve(value); }, - (err) => { + (err: unknown) => { signal.removeEventListener("abort", onAbort); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }, ); }); @@ -236,11 +236,11 @@ function runOnce(mode: Mode, scopeBytes: number, iter: number): void { void subscription; resolve(v); }, - (e) => { + (e: unknown) => { void transcript; void toolMetas; void subscription; - reject(e); + reject(toLintErrorObject(e, "Non-Error rejection")); }, ); }); @@ -382,7 +382,21 @@ async function main(): Promise { process.exit(verdict === "PASS" ? 0 : 1); } -main().catch((err) => { +main().catch((err: unknown) => { process.stderr.write(`harness crashed: ${String(err)}\n${(err as Error)?.stack ?? ""}\n`); process.exit(2); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/firecrawl-compare.ts b/scripts/firecrawl-compare.ts index 5082b397e3b..fb8a35be0f3 100644 --- a/scripts/firecrawl-compare.ts +++ b/scripts/firecrawl-compare.ts @@ -165,7 +165,7 @@ async function run() { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - run().catch((error) => { + run().catch((error: unknown) => { console.error(error); process.exit(1); }); diff --git a/scripts/generate-dependency-release-evidence.mjs b/scripts/generate-dependency-release-evidence.mjs index 4adfce40950..e5e5d5edc51 100644 --- a/scripts/generate-dependency-release-evidence.mjs +++ b/scripts/generate-dependency-release-evidence.mjs @@ -412,7 +412,7 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.met (exitCode) => { process.exitCode = exitCode; }, - (error) => { + /** @param {unknown} error */ (error) => { process.stderr.write(`${error.stack ?? error.message ?? String(error)}\n`); process.exitCode = 1; }, diff --git a/scripts/github/dependency-guard.mjs b/scripts/github/dependency-guard.mjs index b0e370804c6..7628a38cec0 100644 --- a/scripts/github/dependency-guard.mjs +++ b/scripts/github/dependency-guard.mjs @@ -964,8 +964,10 @@ async function main() { } if (import.meta.url === `file://${process.argv[1]}`) { - main().catch((error) => { - console.error(error instanceof Error ? error.message : error); - process.exitCode = 1; - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; + }, + ); } diff --git a/scripts/github/real-behavior-proof-check.mjs b/scripts/github/real-behavior-proof-check.mjs index 5e06ecb2d59..2329ec5005a 100644 --- a/scripts/github/real-behavior-proof-check.mjs +++ b/scripts/github/real-behavior-proof-check.mjs @@ -148,7 +148,10 @@ export async function fetchProofComments({ lastError = error; } } - throw lastError ?? new Error("No GitHub token available for proof comment lookup."); + throw toLintErrorObject( + lastError ?? new Error("No GitHub token available for proof comment lookup."), + "Non-Error thrown", + ); } function isMainModule() { @@ -231,3 +234,17 @@ export const testing = { if (isMainModule()) { await main(); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/lib/bounded-response.mjs b/scripts/lib/bounded-response.mjs index ef2cd3a1773..490a1ed6c48 100644 --- a/scripts/lib/bounded-response.mjs +++ b/scripts/lib/bounded-response.mjs @@ -22,7 +22,10 @@ async function readResponseChunk(reader, label, signal, markCanceled) { markCanceled(); void reader.cancel().catch(() => undefined); reject( - signal.reason instanceof Error ? signal.reason : new Error(`${label} request aborted`), + toLintErrorObject( + signal.reason instanceof Error ? signal.reason : new Error(`${label} request aborted`), + "Non-Error rejection", + ), ); }; signal.addEventListener("abort", onAbort, { once: true }); @@ -92,3 +95,17 @@ export async function readBoundedResponseText(response, label, maxBytes, options return chunks.join(""); } + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/lib/openclaw-test-state.mjs b/scripts/lib/openclaw-test-state.mjs index 2111de02a94..4241b5e8a3e 100644 --- a/scripts/lib/openclaw-test-state.mjs +++ b/scripts/lib/openclaw-test-state.mjs @@ -654,9 +654,11 @@ async function main(argv = process.argv.slice(2)) { const isMain = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]); if (isMain) { - main().catch((error) => { - process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); - process.stderr.write(usage()); - process.exitCode = 1; - }); + main().catch( + /** @param {unknown} error */ (error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.stderr.write(usage()); + process.exitCode = 1; + }, + ); } diff --git a/scripts/openclaw-cross-os-release-checks.ts b/scripts/openclaw-cross-os-release-checks.ts index 3e1cb91deb2..45d8b3c9a25 100644 --- a/scripts/openclaw-cross-os-release-checks.ts +++ b/scripts/openclaw-cross-os-release-checks.ts @@ -3989,7 +3989,10 @@ async function withAllocatedGatewayPort(lane, callback) { await sleep(250 * attempt); } } - throw lastError ?? new Error("Failed to allocate a gateway port."); + throw toLintErrorObject( + lastError ?? new Error("Failed to allocate a gateway port."), + "Non-Error thrown", + ); } function reservePort() { @@ -4024,3 +4027,17 @@ function isAddressInUseError(error) { const message = formatError(error); return message.includes("EADDRINUSE") || /address.+in use/iu.test(message); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/scripts/openclaw-performance-source-summary.mjs b/scripts/openclaw-performance-source-summary.mjs index 95542ccb761..18362cc9644 100644 --- a/scripts/openclaw-performance-source-summary.mjs +++ b/scripts/openclaw-performance-source-summary.mjs @@ -444,7 +444,9 @@ async function main() { } } -main().catch((error) => { - console.error(error instanceof Error ? error.stack : String(error)); - process.exitCode = 1; -}); +main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.stack : String(error)); + process.exitCode = 1; + }, +); diff --git a/scripts/package-openclaw-for-docker.mjs b/scripts/package-openclaw-for-docker.mjs index 8670e519c48..017d1032a7f 100644 --- a/scripts/package-openclaw-for-docker.mjs +++ b/scripts/package-openclaw-for-docker.mjs @@ -126,7 +126,7 @@ function run(command, args, cwd, options = {}) { process.exit(forwardedSignalExitCode); } if (error) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); return; } resolve(value); @@ -346,8 +346,24 @@ async function main() { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - await main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(Number.isInteger(error?.exitCode) ? error.exitCode : 1); - }); + await main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(Number.isInteger(error?.exitCode) ? error.exitCode : 1); + }, + ); +} + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; } diff --git a/scripts/proof-73706-message-sending-session-key.ts b/scripts/proof-73706-message-sending-session-key.ts index 13059c3bfe3..8e643ef8acd 100644 --- a/scripts/proof-73706-message-sending-session-key.ts +++ b/scripts/proof-73706-message-sending-session-key.ts @@ -187,7 +187,7 @@ async function main() { console.log("\n[proof-73706] All runtime assertions passed."); } -main().catch((err) => { +main().catch((err: unknown) => { console.error("[proof-73706] FAILED:", err); process.exitCode = 1; }); diff --git a/scripts/protocol-gen-swift.ts b/scripts/protocol-gen-swift.ts index 3b061c373dd..3984d2d17c5 100644 --- a/scripts/protocol-gen-swift.ts +++ b/scripts/protocol-gen-swift.ts @@ -637,7 +637,7 @@ async function generate() { } } -generate().catch((err) => { +generate().catch((err: unknown) => { console.error(err); process.exit(1); }); diff --git a/scripts/protocol-gen.ts b/scripts/protocol-gen.ts index 865dc2d3bc8..45a52ac4d27 100644 --- a/scripts/protocol-gen.ts +++ b/scripts/protocol-gen.ts @@ -45,7 +45,7 @@ async function main() { await writeJsonSchema(); } -main().catch((err) => { +main().catch((err: unknown) => { console.error(err); process.exit(1); }); diff --git a/scripts/proxy-install-ca.mjs b/scripts/proxy-install-ca.mjs index b2d3549e183..4f11d25771f 100644 --- a/scripts/proxy-install-ca.mjs +++ b/scripts/proxy-install-ca.mjs @@ -49,7 +49,9 @@ async function installCa() { process.stdout.write("Trusted the OpenClaw debug proxy CA in System.keychain.\n"); } -void installCa().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(1); -}); +void installCa().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + }, +); diff --git a/scripts/qa-otel-smoke.ts b/scripts/qa-otel-smoke.ts index bb3fa8f2893..dbae8f1d42f 100644 --- a/scripts/qa-otel-smoke.ts +++ b/scripts/qa-otel-smoke.ts @@ -1348,7 +1348,7 @@ export const testing = { }; if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - main().catch((error) => { + main().catch((error: unknown) => { process.stderr.write( `qa-otel-smoke: ${error instanceof Error ? error.stack || error.message : String(error)}\n`, ); diff --git a/scripts/readability-basic-compare.ts b/scripts/readability-basic-compare.ts index 8ce8e0d7182..e683c936b2c 100644 --- a/scripts/readability-basic-compare.ts +++ b/scripts/readability-basic-compare.ts @@ -60,7 +60,7 @@ async function run() { } } -run().catch((error) => { +run().catch((error: unknown) => { console.error(error); process.exit(1); }); diff --git a/scripts/release-candidate-checklist.mjs b/scripts/release-candidate-checklist.mjs index 479da654533..2b164c9be44 100644 --- a/scripts/release-candidate-checklist.mjs +++ b/scripts/release-candidate-checklist.mjs @@ -764,8 +764,10 @@ async function main() { } if (process.argv[1] === fileURLToPath(import.meta.url)) { - await main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(1); - }); + await main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + }, + ); } diff --git a/scripts/resolve-openclaw-package-candidate.mjs b/scripts/resolve-openclaw-package-candidate.mjs index 13a454519be..f50dd2e8687 100644 --- a/scripts/resolve-openclaw-package-candidate.mjs +++ b/scripts/resolve-openclaw-package-candidate.mjs @@ -900,18 +900,20 @@ async function openHttpsPackageDownloadResponse(parsed, options) { ); request.on("error", reject); request.end(); - }).catch((error) => { - clearTimeout(timeout); - if (error?.name === "AbortError" || error?.code === "ABORT_ERR") { - throw new Error( - `package_url download timed out after ${options.timeoutMs}ms: ${parsed.toString()}`, - { - cause: error, - }, - ); - } - throw error; - }); + }).catch( + /** @param {unknown} error */ (error) => { + clearTimeout(timeout); + if (error?.name === "AbortError" || error?.code === "ABORT_ERR") { + throw new Error( + `package_url download timed out after ${options.timeoutMs}ms: ${parsed.toString()}`, + { + cause: error, + }, + ); + } + throw error; + }, + ); return { close: async () => closeResponseBody(response.body), response, @@ -1197,9 +1199,11 @@ export async function main(argv = process.argv.slice(2)) { } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { - await main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - console.error(usage()); - process.exit(1); - }); + await main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + console.error(usage()); + process.exit(1); + }, + ); } diff --git a/scripts/rtt.ts b/scripts/rtt.ts index 8198e8b6d28..6ebb15ce683 100644 --- a/scripts/rtt.ts +++ b/scripts/rtt.ts @@ -257,7 +257,7 @@ async function main() { } if (import.meta.url === `file://${process.argv[1]}`) { - main().catch((error) => { + main().catch((error: unknown) => { const message = error instanceof Error ? error.message : String(error); process.stderr.write(`[rtt] ${message}\n`); process.exitCode = 1; diff --git a/scripts/run-node.mjs b/scripts/run-node.mjs index 4742e56491c..ae6cab838b6 100644 --- a/scripts/run-node.mjs +++ b/scripts/run-node.mjs @@ -1532,8 +1532,10 @@ export async function runNodeMain(params = {}) { if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { void runNodeMain() .then((code) => process.exit(code)) - .catch((err) => { - console.error(err); - process.exit(1); - }); + .catch( + /** @param {unknown} err */ (err) => { + console.error(err); + process.exit(1); + }, + ); } diff --git a/scripts/secrets/openclaw-bws-resolver.mjs b/scripts/secrets/openclaw-bws-resolver.mjs index b2ac433d6c9..2381e57cb8a 100755 --- a/scripts/secrets/openclaw-bws-resolver.mjs +++ b/scripts/secrets/openclaw-bws-resolver.mjs @@ -6,7 +6,7 @@ const readStdin = () => let input = ""; process.stdin.setEncoding("utf8"); process.stdin.on("data", (chunk) => { - input += chunk; + input += chunk.toString(); }); process.stdin.on("end", () => resolve(input)); process.stdin.on("error", reject); @@ -88,7 +88,9 @@ const main = async () => { process.stdout.write(JSON.stringify({ protocolVersion: 1, values, errors })); }; -main().catch((error) => { - process.stderr.write(`${error.message}\n`); - process.exit(1); -}); +main().catch( + /** @param {unknown} error */ (error) => { + process.stderr.write(`${error.message}\n`); + process.exit(1); + }, +); diff --git a/scripts/sync-moonshot-docs.ts b/scripts/sync-moonshot-docs.ts index b1c05b2ec56..d85919a7f48 100644 --- a/scripts/sync-moonshot-docs.ts +++ b/scripts/sync-moonshot-docs.ts @@ -119,7 +119,7 @@ async function syncMoonshotDocs() { await writeFile(conceptsDoc, conceptsText); } -syncMoonshotDocs().catch((error) => { +syncMoonshotDocs().catch((error: unknown) => { console.error(error); process.exitCode = 1; }); diff --git a/scripts/test-docker-all.mjs b/scripts/test-docker-all.mjs index b5c9daa4f7e..9ff38ebafd8 100644 --- a/scripts/test-docker-all.mjs +++ b/scripts/test-docker-all.mjs @@ -1457,8 +1457,10 @@ async function main() { } if (IS_MAIN) { - await main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(1); - }); + await main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + }, + ); } diff --git a/scripts/test-group-report.mjs b/scripts/test-group-report.mjs index ea4ccecce3b..8f035888575 100644 --- a/scripts/test-group-report.mjs +++ b/scripts/test-group-report.mjs @@ -571,8 +571,10 @@ const isMain = import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href; if (isMain) { - main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + }, + ); } diff --git a/scripts/test-live-media.ts b/scripts/test-live-media.ts index f2c5613e440..799ecefaf5b 100644 --- a/scripts/test-live-media.ts +++ b/scripts/test-live-media.ts @@ -420,7 +420,7 @@ export async function runCli(argv: string[]): Promise { if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { runCli(process.argv.slice(2)) .then((code) => process.exit(code)) - .catch((error) => { + .catch((error: unknown) => { console.error(formatErrorMessage(error)); process.exit(1); }); diff --git a/scripts/test-projects.mjs b/scripts/test-projects.mjs index 535e373ec7a..2f3a5ee1c0e 100644 --- a/scripts/test-projects.mjs +++ b/scripts/test-projects.mjs @@ -378,8 +378,10 @@ function printTestSummary(status, shardCount, durationMs, detail) { ); } -main().catch((error) => { - releaseLockOnce(); - console.error(error); - process.exit(1); -}); +main().catch( + /** @param {unknown} error */ (error) => { + releaseLockOnce(); + console.error(error); + process.exit(1); + }, +); diff --git a/scripts/test-shell-completion.ts b/scripts/test-shell-completion.ts index 8e69a7b12ea..40e995d3c0d 100644 --- a/scripts/test-shell-completion.ts +++ b/scripts/test-shell-completion.ts @@ -217,7 +217,7 @@ async function main() { await installCompletion(status.shell, false, CLI_NAME); } -main().catch((err) => { +main().catch((err: unknown) => { console.error(theme.error(`Error: ${String(err)}`)); process.exit(1); }); diff --git a/scripts/transitive-manifest-risk-report.mjs b/scripts/transitive-manifest-risk-report.mjs index 6a0a7b6d9b4..0d68314eff6 100644 --- a/scripts/transitive-manifest-risk-report.mjs +++ b/scripts/transitive-manifest-risk-report.mjs @@ -668,7 +668,7 @@ export async function main(argv = process.argv.slice(2)) { renderTransitiveManifestRiskMarkdownReport(report), ); const artifactHint = - typeof options.markdownPath === "string" ? " See " + options.markdownPath + "." : ""; + typeof options.markdownPath === "string" ? " See ".concat(options.markdownPath, ".") : ""; process.stdout.write( `INFO transitive manifest risk report: inspected ${report.packageVersions} resolved ` + `package manifests; ${report.findingCount} reported risk signals, ` + @@ -682,7 +682,7 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.met (exitCode) => { process.exitCode = exitCode; }, - (error) => { + /** @param {unknown} error */ (error) => { process.stderr.write(`${error.stack ?? error.message ?? String(error)}\n`); process.exitCode = 1; }, diff --git a/scripts/verify-docker-attestations.mjs b/scripts/verify-docker-attestations.mjs index 8605df0c0e2..af34b658d0d 100644 --- a/scripts/verify-docker-attestations.mjs +++ b/scripts/verify-docker-attestations.mjs @@ -199,8 +199,10 @@ async function main() { } if (import.meta.url === `file://${process.argv[1]}`) { - main().catch((error) => { - console.error(error instanceof Error ? error.message : String(error)); - process.exit(1); - }); + main().catch( + /** @param {unknown} error */ (error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); + }, + ); } diff --git a/scripts/verify-plugin-npm-published-runtime.mjs b/scripts/verify-plugin-npm-published-runtime.mjs index 4c9aa9d7de8..54f156715b9 100644 --- a/scripts/verify-plugin-npm-published-runtime.mjs +++ b/scripts/verify-plugin-npm-published-runtime.mjs @@ -377,10 +377,12 @@ async function main(argv) { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - main(process.argv.slice(2)).catch((error) => { - console.error( - `plugin-npm-published-runtime-check: ${error instanceof Error ? error.message : String(error)}`, - ); - process.exitCode = 1; - }); + main(process.argv.slice(2)).catch( + /** @param {unknown} error */ (error) => { + console.error( + `plugin-npm-published-runtime-check: ${error instanceof Error ? error.message : String(error)}`, + ); + process.exitCode = 1; + }, + ); } diff --git a/scripts/watch-node.mjs b/scripts/watch-node.mjs index ce1b8e08351..aa90d15f9a7 100644 --- a/scripts/watch-node.mjs +++ b/scripts/watch-node.mjs @@ -363,7 +363,7 @@ export async function runWatchMain(params = {}) { if (onSigTerm) { deps.process.off("SIGTERM", onSigTerm); } - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }; const resolveCreateWatcher = async () => { @@ -484,20 +484,38 @@ export async function runWatchMain(params = {}) { startRunner(); startWatcher(); }) - .catch((error) => { - logWatcher(`Failed to acquire watcher lock: ${error?.message ?? "unknown error"}`, deps); - settle(1); - }); + .catch( + /** @param {unknown} error */ (error) => { + logWatcher(`Failed to acquire watcher lock: ${error?.message ?? "unknown error"}`, deps); + settle(1); + }, + ); }); } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { void runWatchMain() .then((code) => process.exit(code)) - .catch((err) => { - if (!isInvalidPackageConfigError(err)) { - console.error(err); - } - process.exit(1); - }); + .catch( + /** @param {unknown} err */ (err) => { + if (!isInvalidPackageConfigError(err)) { + console.error(err); + } + process.exit(1); + }, + ); +} + +function toLintErrorObject(value, fallbackMessage) { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; } diff --git a/scripts/zai-fallback-repro.ts b/scripts/zai-fallback-repro.ts index 87945c73021..a4f5b1c6f0b 100644 --- a/scripts/zai-fallback-repro.ts +++ b/scripts/zai-fallback-repro.ts @@ -242,7 +242,7 @@ function isCliEntrypoint() { } if (isCliEntrypoint()) { - await main().catch((err) => { + await main().catch((err: unknown) => { console.error(err); process.exit(1); }); diff --git a/security/opengrep/compile-rules.mjs b/security/opengrep/compile-rules.mjs index b78104e9bf1..e45dc6f7e6b 100644 --- a/security/opengrep/compile-rules.mjs +++ b/security/opengrep/compile-rules.mjs @@ -596,7 +596,9 @@ async function main() { printSummary(buckets, manifest, opts.outDir); } -main().catch((err) => { - console.error(`compile-rules: error: ${err.message ?? err}`); - process.exit(1); -}); +main().catch( + /** @param {unknown} err */ (err) => { + console.error(`compile-rules: error: ${err.message ?? err}`); + process.exit(1); + }, +); diff --git a/src/acp/control-plane/manager.core.ts b/src/acp/control-plane/manager.core.ts index 6fd31a9145e..78be975b1c4 100644 --- a/src/acp/control-plane/manager.core.ts +++ b/src/acp/control-plane/manager.core.ts @@ -582,7 +582,7 @@ export class AcpSessionManager { } settled = true; cleanup(); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }; const onAbort = () => { if (actorStarted) { @@ -610,3 +610,17 @@ export class AcpSessionManager { throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP operation aborted."); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/acp/control-plane/manager.initialize-session.ts b/src/acp/control-plane/manager.initialize-session.ts index 2bd5cc0ceea..e233eaff341 100644 --- a/src/acp/control-plane/manager.initialize-session.ts +++ b/src/acp/control-plane/manager.initialize-session.ts @@ -165,7 +165,7 @@ async function closeRuntimeAfterInitMetaFailure(params: { handle: params.handle, reason: "init-meta-failed", }) - .catch((closeError) => { + .catch((closeError: unknown) => { logVerbose( `acp-manager: cleanup close failed after metadata write error for ${params.sessionKey}: ${String(closeError)}`, ); diff --git a/src/acp/control-plane/manager.runtime-resume-state.ts b/src/acp/control-plane/manager.runtime-resume-state.ts index df441550b52..b67e9228f76 100644 --- a/src/acp/control-plane/manager.runtime-resume-state.ts +++ b/src/acp/control-plane/manager.runtime-resume-state.ts @@ -184,7 +184,7 @@ export async function tryPrepareFreshManagerRuntimeSession(params: { const backend = params.deps.getRuntimeBackend(configuredBackend || undefined); if (!backend) { if (params.missingBackendError) { - throw params.missingBackendError; + throw toLintErrorObject(params.missingBackendError, "Non-Error thrown"); } return; } @@ -197,3 +197,17 @@ export async function tryPrepareFreshManagerRuntimeSession(params: { ); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/acp/control-plane/manager.test-helpers.ts b/src/acp/control-plane/manager.test-helpers.ts index 78bc4a94de8..5d0a2170350 100644 --- a/src/acp/control-plane/manager.test-helpers.ts +++ b/src/acp/control-plane/manager.test-helpers.ts @@ -88,7 +88,7 @@ export async function expectRejectedRecord( () => { throw new Error("Expected promise to reject."); }, - (error) => { + (error: unknown) => { expectRecordFields(error, expected); }, ); diff --git a/src/acp/control-plane/manager.test.ts b/src/acp/control-plane/manager.test.ts index 2d02e919293..0c5da4b2510 100644 --- a/src/acp/control-plane/manager.test.ts +++ b/src/acp/control-plane/manager.test.ts @@ -595,7 +595,7 @@ describe("AcpSessionManager", () => { const secondOutcome = await Promise.race([ second.then( () => ({ status: "resolved" as const }), - (error) => ({ status: "rejected" as const, error }), + (error: unknown) => ({ status: "rejected" as const, error }), ), new Promise<{ status: "pending" }>((resolve) => { scheduleNativeTimeout(() => resolve({ status: "pending" }), 100); diff --git a/src/acp/control-plane/manager.turn-timeout.ts b/src/acp/control-plane/manager.turn-timeout.ts index 30f452ca666..4f9742eb9ec 100644 --- a/src/acp/control-plane/manager.turn-timeout.ts +++ b/src/acp/control-plane/manager.turn-timeout.ts @@ -49,7 +49,7 @@ export async function awaitTurnWithTimeout(params: { kind: "value" as const, value, }), - (error) => ({ + (error: unknown) => ({ kind: "error" as const, error, }), @@ -162,7 +162,7 @@ async function awaitCleanupWithGrace(params: { () => ({ kind: "done" as const, }), - (error) => ({ + (error: unknown) => ({ kind: "error" as const, error, }), diff --git a/src/acp/control-plane/manager.utils.ts b/src/acp/control-plane/manager.utils.ts index bc6268e0ddf..d92b9f9a627 100644 --- a/src/acp/control-plane/manager.utils.ts +++ b/src/acp/control-plane/manager.utils.ts @@ -44,7 +44,7 @@ export function requireReadySessionMeta(resolution: AcpSessionResolution): Sessi if (resolution.kind === "ready") { return resolution.meta; } - throw resolveAcpSessionResolutionError(resolution); + throw toLintErrorObject(resolveAcpSessionResolutionError(resolution), "Non-Error thrown"); } function normalizeSessionKey(sessionKey: string): string { @@ -121,3 +121,17 @@ export function hasLegacyAcpIdentityProjection(meta: SessionAcpMeta): boolean { Object.hasOwn(raw, "sessionIdsProvisional") ); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/acp/control-plane/spawn.ts b/src/acp/control-plane/spawn.ts index fc769afb285..5f39ffe7c56 100644 --- a/src/acp/control-plane/spawn.ts +++ b/src/acp/control-plane/spawn.ts @@ -27,7 +27,7 @@ export async function cleanupFailedAcpSpawn(params: { handle: params.runtimeCloseHandle.handle, reason: "spawn-failed", }) - .catch((err) => { + .catch((err: unknown) => { logVerbose( `acp-spawn: runtime cleanup close failed for ${params.sessionKey}: ${String(err)}`, ); @@ -43,7 +43,7 @@ export async function cleanupFailedAcpSpawn(params: { allowBackendUnavailable: true, requireAcpSession: false, }) - .catch((err) => { + .catch((err: unknown) => { logVerbose( `acp-spawn: manager cleanup close failed for ${params.sessionKey}: ${String(err)}`, ); @@ -54,7 +54,7 @@ export async function cleanupFailedAcpSpawn(params: { targetSessionKey: params.sessionKey, reason: "spawn-failed", }) - .catch((err) => { + .catch((err: unknown) => { logVerbose( `acp-spawn: binding cleanup unbind failed for ${params.sessionKey}: ${String(err)}`, ); diff --git a/src/acp/server.ts b/src/acp/server.ts index 297ab5fce41..8b10af87f53 100644 --- a/src/acp/server.ts +++ b/src/acp/server.ts @@ -120,7 +120,7 @@ export async function serveAcpGateway(opts: AcpServerOptions = {}): Promise { + await gatewayReady.catch((err: unknown) => { shutdown(); throw err; }); @@ -271,7 +271,7 @@ if (isMainModule({ currentFile: fileURLToPath(import.meta.url) })) { ); } const opts = parseArgs(argv); - serveAcpGateway(opts).catch((err) => { + serveAcpGateway(opts).catch((err: unknown) => { console.error(String(err)); process.exit(1); }); diff --git a/src/acp/translator.prompt-harness.test-support.ts b/src/acp/translator.prompt-harness.test-support.ts index 05188843123..61d05c9699c 100644 --- a/src/acp/translator.prompt-harness.test-support.ts +++ b/src/acp/translator.prompt-harness.test-support.ts @@ -56,7 +56,7 @@ export function observeSettlement(promise: ReturnType const settleSpy = vi.fn(); void promise.then( (value) => settleSpy({ kind: "resolve", value }), - (error) => settleSpy({ kind: "reject", error }), + (error: unknown) => settleSpy({ kind: "reject", error }), ); return settleSpy; } diff --git a/src/acp/translator.ts b/src/acp/translator.ts index 65b5490567c..236c7de6a4c 100644 --- a/src/acp/translator.ts +++ b/src/acp/translator.ts @@ -438,7 +438,7 @@ export class AcpGatewayAgent implements Agent { this.getSessionSnapshot(session.sessionKey), ledgerReplay.complete ? Promise.resolve([]) - : this.getSessionTranscript(session.sessionKey).catch((err) => { + : this.getSessionTranscript(session.sessionKey).catch((err: unknown) => { this.log(`session transcript fallback for ${session.sessionKey}: ${String(err)}`); return []; }), @@ -726,7 +726,7 @@ export class AcpGatewayAgent implements Agent { } }; - void sendWithProvenanceFallback().catch((err) => { + void sendWithProvenanceFallback().catch((err: unknown) => { if (isGatewayCloseError(err) && this.getPendingPrompt(params.sessionId, runId)) { return; } diff --git a/src/agents/agent-bundle-mcp-runtime.ts b/src/agents/agent-bundle-mcp-runtime.ts index 1f4b527a82d..54f96536ea9 100644 --- a/src/agents/agent-bundle-mcp-runtime.ts +++ b/src/agents/agent-bundle-mcp-runtime.ts @@ -201,9 +201,9 @@ function connectWithTimeout( clearTimeout(timer); resolve(value); }, - (error) => { + (error: unknown) => { clearTimeout(timer); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); @@ -1152,3 +1152,17 @@ export const testing = { resolveSessionMcpRuntimeIdleTtlMs, }; export { testing as __testing }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/agent-tools-agent-config.test.ts b/src/agents/agent-tools-agent-config.test.ts index 5a0a51ffccf..87914c32e39 100644 --- a/src/agents/agent-tools-agent-config.test.ts +++ b/src/agents/agent-tools-agent-config.test.ts @@ -251,9 +251,9 @@ describe("Agent-specific tool filtering", () => { ); const readError = await fs.readFile(escapedPath, "utf8").then( () => undefined, - (err: NodeJS.ErrnoException) => err, + (err: unknown) => err, ); - expect(readError?.code).toBe("ENOENT"); + expect(readError).toMatchObject({ code: "ENOENT" }); }); }); diff --git a/src/agents/agent-tools.before-tool-call.e2e.test.ts b/src/agents/agent-tools.before-tool-call.e2e.test.ts index 1e322e12ddf..941accd55b5 100644 --- a/src/agents/agent-tools.before-tool-call.e2e.test.ts +++ b/src/agents/agent-tools.before-tool-call.e2e.test.ts @@ -945,7 +945,10 @@ describe("before_tool_call requireApproval handling", () => { setActivePluginRegistry(createEmptyPluginRegistry()); }); - async function runAbortDuringApprovalWait(options?: { onResolution?: ReturnType }) { + async function runAbortDuringApprovalWait(options?: { + abortReason?: unknown; + onResolution?: ReturnType; + }) { hookRunner.runBeforeToolCall.mockResolvedValue({ requireApproval: { title: "Abortable", @@ -957,7 +960,7 @@ describe("before_tool_call requireApproval handling", () => { const controller = new AbortController(); mockCallGateway.mockResolvedValueOnce({ id: "server-id-abort", status: "accepted" }); mockCallGateway.mockImplementationOnce(() => new Promise(() => {})); - setTimeout(() => controller.abort(new Error("run cancelled")), 10); + setTimeout(() => controller.abort(options?.abortReason ?? new Error("run cancelled")), 10); return await runBeforeToolCallHook({ toolName: "bash", @@ -1442,6 +1445,13 @@ describe("before_tool_call requireApproval handling", () => { expect(mockCallGateway).toHaveBeenCalledTimes(2); }); + it("classifies non-Error abort reasons as run abort cancellation", async () => { + const result = await runAbortDuringApprovalWait({ abortReason: "sessions_yield" }); + + expect(result.blocked).toBe(true); + expect(result).toHaveProperty("reason", "Approval cancelled (run aborted)"); + }); + it("removes abort listener after waitDecision resolves", async () => { hookRunner.runBeforeToolCall.mockResolvedValue({ requireApproval: { diff --git a/src/agents/agent-tools.before-tool-call.ts b/src/agents/agent-tools.before-tool-call.ts index 2932428c03c..ece2566d825 100644 --- a/src/agents/agent-tools.before-tool-call.ts +++ b/src/agents/agent-tools.before-tool-call.ts @@ -72,7 +72,10 @@ export function isAbortSignalCancellation(err: unknown, signal?: AbortSignal): b if (err === signal.reason) { return true; } - return err instanceof Error && err.name === "AbortError"; + return ( + err instanceof Error && + (err.name === "AbortError" || ("cause" in err && err.cause === signal.reason)) + ); } export type HookContext = { @@ -409,7 +412,7 @@ function notifyPluginApprovalResolution( return; } try { - void Promise.resolve(onResolution(resolution)).catch((err) => { + void Promise.resolve(onResolution(resolution)).catch((err: unknown) => { log.warn(`plugin onResolution callback failed: ${String(err)}`); }); } catch (err) { @@ -497,10 +500,10 @@ async function requestPluginToolApproval(params: { let onAbort: (() => void) | undefined; const abortPromise = new Promise((_, reject) => { if (params.signal!.aborted) { - reject(params.signal!.reason); + reject(toLintErrorObject(params.signal!.reason, "Non-Error rejection")); return; } - onAbort = () => reject(params.signal!.reason); + onAbort = () => reject(toLintErrorObject(params.signal!.reason, "Non-Error rejection")); params.signal!.addEventListener("abort", onAbort, { once: true }); }); try { @@ -1332,3 +1335,17 @@ export const testing = { isPlainObject, }; export { testing as __testing }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value, { cause: value }); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/agent-tools.read.ts b/src/agents/agent-tools.read.ts index 6d50ca609f9..fe09d6939cd 100644 --- a/src/agents/agent-tools.read.ts +++ b/src/agents/agent-tools.read.ts @@ -723,7 +723,10 @@ async function assertSandboxPathWithinAnyRoot(params: { firstRootEscapeError ??= error; } } - throw firstRootEscapeError ?? new Error("Path guard has no configured roots."); + throw toLintErrorObject( + firstRootEscapeError ?? new Error("Path guard has no configured roots."), + "Non-Error thrown", + ); } export function wrapToolWorkspaceRootGuardWithOptions( @@ -1086,3 +1089,17 @@ function createFsAccessError(code: string, filePath: string): NodeJS.ErrnoExcept error.code = code; return error; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/anthropic-transport-stream.ts b/src/agents/anthropic-transport-stream.ts index 50c7b79c300..2774fa82d56 100644 --- a/src/agents/anthropic-transport-stream.ts +++ b/src/agents/anthropic-transport-stream.ts @@ -593,7 +593,7 @@ function readAnthropicSseChunk( } settled = true; signal.removeEventListener("abort", onAbort); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); @@ -1404,3 +1404,17 @@ export function createAnthropicMessagesTransportStreamFn(): StreamFn { return eventStream as ReturnType; }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/api-key-rotation.ts b/src/agents/api-key-rotation.ts index 09fce0c97f0..150b4f33c73 100644 --- a/src/agents/api-key-rotation.ts +++ b/src/agents/api-key-rotation.ts @@ -93,5 +93,19 @@ export async function executeWithApiKeyRotation( if (lastError === undefined) { throw new Error(`Failed to run API request for ${params.provider}.`); } - throw lastError; + throw toLintErrorObject(lastError, "Non-Error thrown"); +} + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; } diff --git a/src/agents/apply-patch-update.ts b/src/agents/apply-patch-update.ts index b965d6ca68f..d8c13229fdc 100644 --- a/src/agents/apply-patch-update.ts +++ b/src/agents/apply-patch-update.ts @@ -1,4 +1,5 @@ import fs from "node:fs/promises"; +import { formatErrorMessage } from "../infra/errors.js"; type UpdateFileChunk = { changeContext?: string; @@ -17,8 +18,8 @@ export async function applyUpdateHunk( options?: { readFile?: (filePath: string) => Promise }, ): Promise { const reader = options?.readFile ?? defaultReadFile; - const originalContents = await reader(filePath).catch((err) => { - throw new Error(`Failed to read file to update ${filePath}: ${err}`); + const originalContents = await reader(filePath).catch((err: unknown) => { + throw new Error(`Failed to read file to update ${filePath}: ${formatErrorMessage(err)}`); }); const originalLines = originalContents.split("\n"); diff --git a/src/agents/auth-profiles/oauth-refresh-queue.test.ts b/src/agents/auth-profiles/oauth-refresh-queue.test.ts index 0d9da9f71de..b1207277212 100644 --- a/src/agents/auth-profiles/oauth-refresh-queue.test.ts +++ b/src/agents/auth-profiles/oauth-refresh-queue.test.ts @@ -91,12 +91,12 @@ describe("OAuth refresh in-process queue", () => { store: ensureAuthProfileStore(agentDir), profileId, agentDir, - }).catch((e) => e), + }).catch((e: unknown) => e), resolveApiKeyForProfileInTest(resolveApiKeyForProfile, { store: ensureAuthProfileStore(agentDir), profileId, agentDir, - }).catch((e) => e), + }).catch((e: unknown) => e), ]); expect(first).toBeInstanceOf(Error); diff --git a/src/agents/bash-tools.exec-host-shared.ts b/src/agents/bash-tools.exec-host-shared.ts index 2ba21aa4004..5a9d117d1b0 100644 --- a/src/agents/bash-tools.exec-host-shared.ts +++ b/src/agents/bash-tools.exec-host-shared.ts @@ -439,7 +439,7 @@ export async function sendExecApprovalFollowupResult( idempotencyKey: runtimeHandoff.idempotencyKey, } : {}), - }).catch((error) => { + }).catch((error: unknown) => { const message = formatErrorMessage(error); const key = `${target.approvalId}:${message}`; if (!rememberExecApprovalFollowupFailureKey(key)) { diff --git a/src/agents/bash-tools.exec-runtime.ts b/src/agents/bash-tools.exec-runtime.ts index ca290223fea..c425e0dccb0 100644 --- a/src/agents/bash-tools.exec-runtime.ts +++ b/src/agents/bash-tools.exec-runtime.ts @@ -991,7 +991,7 @@ export async function runExecProcess(opts: { }); return outcome; }) - .catch((err): ExecProcessOutcome => { + .catch((err: unknown): ExecProcessOutcome => { updatesDisabled = true; markExited(session, null, null, "failed"); maybeNotifyOnExit(session, "failed"); diff --git a/src/agents/bash-tools.exec.ts b/src/agents/bash-tools.exec.ts index c21d86d03fd..f7a5b7ffc94 100644 --- a/src/agents/bash-tools.exec.ts +++ b/src/agents/bash-tools.exec.ts @@ -1816,7 +1816,7 @@ export function createExecTool( }), ); }) - .catch((err) => { + .catch((err: unknown) => { cleanupToolRunListeners(); if (yielded || run.session.backgrounded) { return; diff --git a/src/agents/cli-runner.ts b/src/agents/cli-runner.ts index f05c4b7a6fb..704b419be17 100644 --- a/src/agents/cli-runner.ts +++ b/src/agents/cli-runner.ts @@ -215,7 +215,7 @@ async function persistApprovedCliUserTurnTranscript(params: RunCliAgentParams): try { const notification = params.onUserMessagePersisted?.(persisted.message); if (notification) { - void Promise.resolve(notification).catch((error) => { + void Promise.resolve(notification).catch((error: unknown) => { log.warn(`CLI user turn persistence notification failed: ${formatErrorMessage(error)}`); }); } diff --git a/src/agents/cli-runner/claude-live-session.ts b/src/agents/cli-runner/claude-live-session.ts index 327ad1a8861..e03642ecee7 100644 --- a/src/agents/cli-runner/claude-live-session.ts +++ b/src/agents/cli-runner/claude-live-session.ts @@ -1094,7 +1094,7 @@ async function createClaudeLiveSession(params: { }; void managedRun.wait().then( (exit) => handleClaudeExit(session, exit.exitCode), - (error) => { + (error: unknown) => { if (session) { closeLiveSession(session, "abort", error); } diff --git a/src/agents/command/delivery.ts b/src/agents/command/delivery.ts index 6d768aca53f..320101d4420 100644 --- a/src/agents/command/delivery.ts +++ b/src/agents/command/delivery.ts @@ -660,7 +660,7 @@ export async function deliverAgentCommandResult( }; if (strictPreDeliveryError) { emitJsonEnvelope(deliveryStatus); - throw strictPreDeliveryError; + throw toLintErrorObject(strictPreDeliveryError, "Non-Error thrown"); } const deliveryPayloads = projectOutboundPayloadPlanForOutbound(outboundPayloadPlan); @@ -748,3 +748,17 @@ export async function deliverAgentCommandResult( deliveryStatus, }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/compaction-planning-worker.ts b/src/agents/compaction-planning-worker.ts index 8528e54a05c..94cd99fdb11 100644 --- a/src/agents/compaction-planning-worker.ts +++ b/src/agents/compaction-planning-worker.ts @@ -55,7 +55,12 @@ function runCompactionPlanningWorker(params: { workerUrl?: URL; }): Promise { if (params.signal?.aborted) { - return Promise.reject(params.signal.reason ?? new Error("compaction planning aborted")); + return Promise.reject( + toLintErrorObject( + params.signal.reason ?? new Error("compaction planning aborted"), + "Non-Error rejection", + ), + ); } const workerUrl = params.workerUrl ?? resolveCompactionPlanningWorkerUrl(); @@ -93,7 +98,16 @@ function runCompactionPlanningWorker(params: { ); const abort = () => { - settle(() => reject(params.signal?.reason ?? new Error("compaction planning aborted")), true); + settle( + () => + reject( + toLintErrorObject( + params.signal?.reason ?? new Error("compaction planning aborted"), + "Non-Error rejection", + ), + ), + true, + ); }; const settle = (finish: () => void, terminate: boolean) => { @@ -343,3 +357,17 @@ export const compactionPlanningWorkerTesting = { runCompactionPlanningWorker, CompactionPlanningWorkerError, }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/context-engine-maintenance.ts b/src/agents/embedded-agent-runner/context-engine-maintenance.ts index e9b63cc2faa..9bdbe51a4b3 100644 --- a/src/agents/embedded-agent-runner/context-engine-maintenance.ts +++ b/src/agents/embedded-agent-runner/context-engine-maintenance.ts @@ -665,7 +665,7 @@ function scheduleDeferredTurnMaintenance( } }; const trackedPromise = runPromise - .catch((err) => { + .catch((err: unknown) => { params.onScheduleFailure?.(err); markDeferredTurnMaintenanceTaskScheduleFailure({ sessionKey, @@ -673,7 +673,7 @@ function scheduleDeferredTurnMaintenance( error: err, }); }) - .then(cleanupDeferredTurnMaintenance, async (err) => { + .then(cleanupDeferredTurnMaintenance, async (err: unknown) => { await cleanupDeferredTurnMaintenance(); throw err; }); diff --git a/src/agents/embedded-agent-runner/run.ts b/src/agents/embedded-agent-runner/run.ts index c0cd6432cb9..7572a61f940 100644 --- a/src/agents/embedded-agent-runner/run.ts +++ b/src/agents/embedded-agent-runner/run.ts @@ -2422,7 +2422,7 @@ export async function runEmbeddedAgent( !hasRecoverableCodexAppServerTimeoutOutcome && !shouldSurfaceCodexCompletionTimeout ) { - throw promptError; + throw toLintErrorObject(promptError, "Prompt failed"); } } @@ -2594,7 +2594,7 @@ export async function runEmbeddedAgent( profileId: failedPromptProfileId, reason: promptProfileFailureReason, modelId, - }).catch((err) => { + }).catch((err: unknown) => { log.warn(`prompt profile failure mark failed: ${String(err)}`); }); } @@ -2686,7 +2686,7 @@ export async function runEmbeddedAgent( }); logPromptFailoverDecision("surface_error"); } - throw promptError; + throw toLintErrorObject(promptError, "Prompt failed"); } const assistantForFailover = currentAttemptAssistant ?? sessionAssistantForCandidate; @@ -3653,3 +3653,17 @@ function resolveAuthProfileStateProvider( const idProvider = profileId.split(":", 1)[0]?.trim(); return idProvider || fallbackProvider; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/abortable.ts b/src/agents/embedded-agent-runner/run/abortable.ts index a3f4c705f80..5a2427eb183 100644 --- a/src/agents/embedded-agent-runner/run/abortable.ts +++ b/src/agents/embedded-agent-runner/run/abortable.ts @@ -29,10 +29,24 @@ export function abortable(signal: AbortSignal, promise: Promise): Promise< signal.removeEventListener("abort", onAbort); resolve(value); }, - (err) => { + (err: unknown) => { signal.removeEventListener("abort", onAbort); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }, ); }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/attempt-abort.ts b/src/agents/embedded-agent-runner/run/attempt-abort.ts index db44dfc0e61..9c4cf72aae0 100644 --- a/src/agents/embedded-agent-runner/run/attempt-abort.ts +++ b/src/agents/embedded-agent-runner/run/attempt-abort.ts @@ -10,7 +10,7 @@ export function releaseEmbeddedAttemptSessionLockForAbort(params: { runId: string; abortKind: "abort" | "timeout abort"; }): void { - void params.sessionLockController.releaseHeldLockForAbort().catch((err) => { + void params.sessionLockController.releaseHeldLockForAbort().catch((err: unknown) => { params.log.warn( `failed to release session lock on ${params.abortKind}: runId=${params.runId} ${String(err)}`, ); diff --git a/src/agents/embedded-agent-runner/run/attempt.async-tasks.ts b/src/agents/embedded-agent-runner/run/attempt.async-tasks.ts index 20d84d7d60d..10b5527b48e 100644 --- a/src/agents/embedded-agent-runner/run/attempt.async-tasks.ts +++ b/src/agents/embedded-agent-runner/run/attempt.async-tasks.ts @@ -71,9 +71,9 @@ async function sleepWithAbort( signal.removeEventListener("abort", onAbort); resolve(); }, - (err) => { + (err: unknown) => { signal.removeEventListener("abort", onAbort); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }, ); }); @@ -213,3 +213,17 @@ export async function waitForCompletionRequiredAsyncTasks(params: { } } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts b/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts index 0e3889f9380..2ab1dbc5ab7 100644 --- a/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts +++ b/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts @@ -635,7 +635,7 @@ export function wrapStreamFnWithDiagnosticModelCallEvents( if (isPromiseLike(result)) { return result.then( (resolved) => observeModelCallResult(resolved, eventBase, startedAt, state), - (err) => { + (err: unknown) => { emitModelCallError(eventBase, startedAt, state, modelCallErrorFields(err)); throw err; }, diff --git a/src/agents/embedded-agent-runner/run/attempt.queue-message.ts b/src/agents/embedded-agent-runner/run/attempt.queue-message.ts index 16b30f3875b..88269f9491a 100644 --- a/src/agents/embedded-agent-runner/run/attempt.queue-message.ts +++ b/src/agents/embedded-agent-runner/run/attempt.queue-message.ts @@ -124,7 +124,7 @@ export async function steerAndWaitForTranscriptCommit( } unsubscribe?.(); if (err) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); return; } resolve(); @@ -204,3 +204,17 @@ export async function steerActiveSessionWithOptionalDeliveryWait( options.deliveryTimeoutMs ?? DEFAULT_QUEUE_TRANSCRIPT_COMMIT_TIMEOUT_MS, ); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/attempt.session-lock.ts b/src/agents/embedded-agent-runner/run/attempt.session-lock.ts index e01634ca409..5af91c4b68d 100644 --- a/src/agents/embedded-agent-runner/run/attempt.session-lock.ts +++ b/src/agents/embedded-agent-runner/run/attempt.session-lock.ts @@ -377,7 +377,9 @@ function waitForSessionFileOwnerRelease(params: { signal?: AbortSignal; }): Promise { if (params.signal?.aborted) { - return Promise.reject(abortOwnerWaitReason(params.signal)); + return Promise.reject( + toLintErrorObject(abortOwnerWaitReason(params.signal), "Non-Error rejection"), + ); } return new Promise((resolve, reject) => { const waiter: SessionFileOwnerWaiter = { @@ -400,7 +402,7 @@ function waitForSessionFileOwnerRelease(params: { }; waiter.reject = (error) => { cleanup(); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }; if (params.timeoutMs !== undefined && Number.isFinite(params.timeoutMs)) { waiter.timer = setTimeout( @@ -1044,3 +1046,17 @@ export function installPromptSubmissionLockRelease(params: { wrappedStreamFn["__openclawSessionLockPromptReleaseInstalled"] = true; agent.streamFn = wrappedStreamFn; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/attempt.sessions-yield.ts b/src/agents/embedded-agent-runner/run/attempt.sessions-yield.ts index 890165e245c..283f42f81a8 100644 --- a/src/agents/embedded-agent-runner/run/attempt.sessions-yield.ts +++ b/src/agents/embedded-agent-runner/run/attempt.sessions-yield.ts @@ -25,7 +25,7 @@ export async function waitForSessionsYieldAbortSettle(params: { const outcome = await Promise.race([ params.settlePromise .then(() => "settled" as const) - .catch((err) => { + .catch((err: unknown) => { log.warn( `sessions_yield abort settle failed: runId=${params.runId} sessionId=${params.sessionId} err=${String(err)}`, ); diff --git a/src/agents/embedded-agent-runner/run/attempt.stop-reason-recovery.ts b/src/agents/embedded-agent-runner/run/attempt.stop-reason-recovery.ts index 11f98f27696..bb0d2801e7b 100644 --- a/src/agents/embedded-agent-runner/run/attempt.stop-reason-recovery.ts +++ b/src/agents/embedded-agent-runner/run/attempt.stop-reason-recovery.ts @@ -142,7 +142,7 @@ export function wrapStreamFnHandleSensitiveStopReason(baseFn: StreamFn): StreamF if (maybeStream && typeof maybeStream === "object" && "then" in maybeStream) { return Promise.resolve(maybeStream).then( (stream) => wrapStreamHandleUnhandledStopReason(model, stream), - (err) => { + (err: unknown) => { const normalizedMessage = normalizeUnhandledStopReasonMessage(formatErrorMessage(err)); if (!normalizedMessage) { throw err; diff --git a/src/agents/embedded-agent-runner/run/attempt.subscription-cleanup.ts b/src/agents/embedded-agent-runner/run/attempt.subscription-cleanup.ts index 5eb771884e3..db5fde09410 100644 --- a/src/agents/embedded-agent-runner/run/attempt.subscription-cleanup.ts +++ b/src/agents/embedded-agent-runner/run/attempt.subscription-cleanup.ts @@ -26,7 +26,7 @@ async function waitForEmbeddedAbortSettle(params: { const outcome = await Promise.race([ params.promise .then(() => "settled" as const) - .catch((err) => { + .catch((err: unknown) => { log.warn( `embedded abort settle failed: runId=${params.runId} sessionId=${params.sessionId} err=${String(err)}`, ); @@ -123,6 +123,20 @@ export async function cleanupEmbeddedAttemptResources(params: { } if (sessionLockReleaseError) { - throw sessionLockReleaseError; + throw toLintErrorObject(sessionLockReleaseError, "Non-Error thrown"); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/attempt.ts b/src/agents/embedded-agent-runner/run/attempt.ts index dc22a327072..72791b83dcc 100644 --- a/src/agents/embedded-agent-runner/run/attempt.ts +++ b/src/agents/embedded-agent-runner/run/attempt.ts @@ -4010,7 +4010,7 @@ export async function runEmbeddedAttempt( ...buildAgentHookContextChannelFields(params), }, ) - .catch((err) => { + .catch((err: unknown) => { log.warn(`llm_input hook failed: ${String(err)}`); }); } @@ -4798,7 +4798,7 @@ export async function runEmbeddedAttempt( ...buildAgentHookContextChannelFields(params), }, ) - .catch((err) => { + .catch((err: unknown) => { log.warn(`llm_output hook failed: ${String(err)}`); }); } @@ -5120,7 +5120,7 @@ export async function runEmbeddedAttempt( }), ); } else { - await Promise.reject(cleanupFailure); + await Promise.reject(toLintErrorObject(cleanupFailure, "Non-Error rejection")); } } } @@ -5150,3 +5150,17 @@ export async function runEmbeddedAttempt( restoreSkillEnv?.(); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/auth-controller.ts b/src/agents/embedded-agent-runner/run/auth-controller.ts index 2341dbff83d..ca867395b4b 100644 --- a/src/agents/embedded-agent-runner/run/auth-controller.ts +++ b/src/agents/embedded-agent-runner/run/auth-controller.ts @@ -216,7 +216,7 @@ export function createEmbeddedRunAuthController(params: { ); } })() - .catch((err) => { + .catch((err: unknown) => { const runtimeModel = params.getRuntimeModel(); params.log.warn( `Runtime auth refresh failed for ${runtimeModel.provider}: ${formatErrorMessage(err)}`, diff --git a/src/agents/embedded-agent-runner/run/llm-idle-timeout.test.ts b/src/agents/embedded-agent-runner/run/llm-idle-timeout.test.ts index 02cbb2b003c..d4075200ef2 100644 --- a/src/agents/embedded-agent-runner/run/llm-idle-timeout.test.ts +++ b/src/agents/embedded-agent-runner/run/llm-idle-timeout.test.ts @@ -359,7 +359,7 @@ describe("streamWithIdleTimeout", () => { streamSignal = options?.signal; return new Promise((_resolve, reject) => { streamSignal?.addEventListener("abort", () => { - reject(streamSignal?.reason); + reject(toLintErrorObject(streamSignal?.reason, "Non-Error rejection")); }); }); }); @@ -489,3 +489,17 @@ describe("streamWithIdleTimeout", () => { expect((timeoutError as Error).message).toMatch(/LLM idle timeout/); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-runner/run/llm-idle-timeout.ts b/src/agents/embedded-agent-runner/run/llm-idle-timeout.ts index 74f2ecd5f0c..61135a89bea 100644 --- a/src/agents/embedded-agent-runner/run/llm-idle-timeout.ts +++ b/src/agents/embedded-agent-runner/run/llm-idle-timeout.ts @@ -314,7 +314,10 @@ export function streamWithIdleTimeout( onThrow(streamIterator, error) { clearTimer(); cleanupSourceSignal(); - return streamIterator.throw?.(error) ?? Promise.reject(error); + return ( + streamIterator.throw?.(error) ?? + Promise.reject(toLintErrorObject(error, "Non-Error rejection")) + ); }, }); }; @@ -341,7 +344,7 @@ export function streamWithIdleTimeout( clearStreamPromiseTimer(); return wrapStream(stream); }, - (error) => { + (error: unknown) => { clearStreamPromiseTimer(); cleanupSourceSignal(); throw error; @@ -351,3 +354,17 @@ export function streamWithIdleTimeout( return wrapStream(maybeStream); }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/embedded-agent-subscribe.handlers.compaction.ts b/src/agents/embedded-agent-subscribe.handlers.compaction.ts index c7398c6278b..8e05c9a68ec 100644 --- a/src/agents/embedded-agent-subscribe.handlers.compaction.ts +++ b/src/agents/embedded-agent-subscribe.handlers.compaction.ts @@ -74,7 +74,7 @@ export function handleCompactionStart( sessionKey: ctx.params.sessionKey, }, ) - .catch((err) => { + .catch((err: unknown) => { ctx.log.warn(`before_compaction hook failed: ${String(err)}`); }); } @@ -113,7 +113,7 @@ export function handleCompactionEnd(ctx: EmbeddedAgentSubscribeContext, evt: Com agentId: ctx.params.agentId, configStore: ctx.params.config?.session?.store, observedCompactionCount, - }).catch((err) => { + }).catch((err: unknown) => { ctx.log.warn(`late compaction count reconcile failed: ${String(err)}`); }); } @@ -162,7 +162,7 @@ export function handleCompactionEnd(ctx: EmbeddedAgentSubscribeContext, evt: Com }, { sessionKey: ctx.params.sessionKey }, ) - .catch((err) => { + .catch((err: unknown) => { ctx.log.warn(`after_compaction hook failed: ${String(err)}`); }); } diff --git a/src/agents/embedded-agent-subscribe.handlers.lifecycle.ts b/src/agents/embedded-agent-subscribe.handlers.lifecycle.ts index 8c62acb76ee..9df03e69e08 100644 --- a/src/agents/embedded-agent-subscribe.handlers.lifecycle.ts +++ b/src/agents/embedded-agent-subscribe.handlers.lifecycle.ts @@ -231,7 +231,7 @@ export function handleAgentEnd(ctx: EmbeddedAgentSubscribeContext): void | Promi } if (isPromiseLike(beforeLifecycleTerminal)) { return Promise.resolve(beforeLifecycleTerminal) - .catch((err) => { + .catch((err: unknown) => { ctx.log.debug(`before lifecycle terminal failed: ${String(err)}`); }) .then(() => { @@ -251,7 +251,7 @@ export function handleAgentEnd(ctx: EmbeddedAgentSubscribeContext): void | Promi if (isPromiseLike(flushPendingMediaAndChannelResult)) { return Promise.resolve(flushPendingMediaAndChannelResult).then( () => emitLifecycleTerminalOnce(), - (error) => { + (error: unknown) => { const emitted = emitLifecycleTerminalOnce(); if (isPromiseLike(emitted)) { return Promise.resolve(emitted).then(() => { diff --git a/src/agents/embedded-agent-subscribe.handlers.messages.ts b/src/agents/embedded-agent-subscribe.handlers.messages.ts index 2e55c2da376..c21e932cee1 100644 --- a/src/agents/embedded-agent-subscribe.handlers.messages.ts +++ b/src/agents/embedded-agent-subscribe.handlers.messages.ts @@ -819,7 +819,7 @@ export function handleMessageUpdate( const assistantMessageIndex = ctx.state.assistantMessageIndex; void Promise.resolve() .then(() => ctx.flushBlockReplyBuffer({ assistantMessageIndex, final: true })) - .catch((err) => { + .catch((err: unknown) => { ctx.log.debug(`text_end block reply flush failed: ${String(err)}`); }); } @@ -1032,7 +1032,7 @@ export function handleMessageEnd( final: true, }); if (isPromiseLike(flushBlockReplyBufferResult)) { - void flushBlockReplyBufferResult.catch((err) => { + void flushBlockReplyBufferResult.catch((err: unknown) => { ctx.log.debug(`message_end block reply flush failed: ${String(err)}`); }); } diff --git a/src/agents/embedded-agent-subscribe.handlers.tools.ts b/src/agents/embedded-agent-subscribe.handlers.tools.ts index 1d2aab92909..5951117d036 100644 --- a/src/agents/embedded-agent-subscribe.handlers.tools.ts +++ b/src/agents/embedded-agent-subscribe.handlers.tools.ts @@ -1554,7 +1554,7 @@ export async function handleToolExecutionEnd( runId, toolCallId, }) - .catch((err) => { + .catch((err: unknown) => { ctx.log.warn(`after_tool_call hook failed: tool=${toolName} error=${String(err)}`); }); } diff --git a/src/agents/embedded-agent-subscribe.handlers.ts b/src/agents/embedded-agent-subscribe.handlers.ts index 19bdc25a98d..04d731176f2 100644 --- a/src/agents/embedded-agent-subscribe.handlers.ts +++ b/src/agents/embedded-agent-subscribe.handlers.ts @@ -42,7 +42,7 @@ export function createEmbeddedAgentSessionEventHandler(ctx: EmbeddedAgentSubscri return; } const task = result - .catch((err) => { + .catch((err: unknown) => { ctx.log.debug(`${evt.type} handler failed: ${String(err)}`); }) .finally(() => { @@ -58,7 +58,7 @@ export function createEmbeddedAgentSessionEventHandler(ctx: EmbeddedAgentSubscri const task = pendingEventChain .then(() => run()) - .catch((err) => { + .catch((err: unknown) => { ctx.log.debug(`${evt.type} handler failed: ${String(err)}`); }) .finally(() => { diff --git a/src/agents/embedded-agent-subscribe.ts b/src/agents/embedded-agent-subscribe.ts index 7ba2cb32157..27528a9b214 100644 --- a/src/agents/embedded-agent-subscribe.ts +++ b/src/agents/embedded-agent-subscribe.ts @@ -272,7 +272,7 @@ export function subscribeEmbeddedAgentSession(params: SubscribeEmbeddedAgentSess if (!isPromiseLike(maybeTask)) { return true; } - const task = Promise.resolve(maybeTask).catch((err) => { + const task = Promise.resolve(maybeTask).catch((err: unknown) => { log.warn(`block reply callback failed: ${String(err)}`); }); pendingBlockReplyTasks.add(task); @@ -452,7 +452,7 @@ export function subscribeEmbeddedAgentSession(params: SubscribeEmbeddedAgentSess state.compactionRetryReject = reject; }); // Prevent unhandled rejection if rejected after all consumers have resolved - state.compactionRetryPromise.catch((err) => { + state.compactionRetryPromise.catch((err: unknown) => { log.debug(`compaction promise rejected (no waiter): ${String(err)}`); }); } diff --git a/src/agents/harness/lifecycle-hook-helpers.ts b/src/agents/harness/lifecycle-hook-helpers.ts index 1ac092b74e2..41df893d765 100644 --- a/src/agents/harness/lifecycle-hook-helpers.ts +++ b/src/agents/harness/lifecycle-hook-helpers.ts @@ -75,9 +75,11 @@ export function runAgentHarnessLlmInputHook(params: { if (!hookRunner?.hasHooks("llm_input") || typeof hookRunner.runLlmInput !== "function") { return; } - void hookRunner.runLlmInput(params.event, buildAgentHookContext(params.ctx)).catch((error) => { - log.warn(`llm_input hook failed: ${String(error)}`); - }); + void hookRunner + .runLlmInput(params.event, buildAgentHookContext(params.ctx)) + .catch((error: unknown) => { + log.warn(`llm_input hook failed: ${String(error)}`); + }); } export function runAgentHarnessLlmOutputHook(params: { @@ -89,9 +91,11 @@ export function runAgentHarnessLlmOutputHook(params: { if (!hookRunner?.hasHooks("llm_output") || typeof hookRunner.runLlmOutput !== "function") { return; } - void hookRunner.runLlmOutput(params.event, buildAgentHookContext(params.ctx)).catch((error) => { - log.warn(`llm_output hook failed: ${String(error)}`); - }); + void hookRunner + .runLlmOutput(params.event, buildAgentHookContext(params.ctx)) + .catch((error: unknown) => { + log.warn(`llm_output hook failed: ${String(error)}`); + }); } async function executeAgentHarnessAgentEndHook(params: { diff --git a/src/agents/harness/native-hook-relay.ts b/src/agents/harness/native-hook-relay.ts index be03bdb0b8c..9684f0e34ed 100644 --- a/src/agents/harness/native-hook-relay.ts +++ b/src/agents/harness/native-hook-relay.ts @@ -1192,7 +1192,7 @@ function postNativeHookRelayBridgeRecord(params: { const rejectOnce = (error: unknown) => { if (!settled) { settled = true; - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); } }; const req = httpRequest( @@ -2017,10 +2017,10 @@ async function waitForNativeHookRelayApprovalDecision(params: { let onAbort: (() => void) | undefined; const abortPromise = new Promise((_, reject) => { if (params.signal!.aborted) { - reject(params.signal!.reason); + reject(toLintErrorObject(params.signal!.reason, "Non-Error rejection")); return; } - onAbort = () => reject(params.signal!.reason); + onAbort = () => reject(toLintErrorObject(params.signal!.reason, "Non-Error rejection")); params.signal!.addEventListener("abort", onAbort, { once: true }); }); try { @@ -2331,3 +2331,17 @@ export const testing = { }, } as const; export { testing as __testing }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/harness/prompt-compaction-hook-helpers.ts b/src/agents/harness/prompt-compaction-hook-helpers.ts index 88329e823e5..76ed9a7d645 100644 --- a/src/agents/harness/prompt-compaction-hook-helpers.ts +++ b/src/agents/harness/prompt-compaction-hook-helpers.ts @@ -36,13 +36,13 @@ export async function resolveAgentHarnessBeforePromptBuildResult(params: { }; const promptBuildResult = hookRunner.hasHooks("before_prompt_build") - ? await hookRunner.runBeforePromptBuild(promptEvent, hookCtx).catch((error) => { + ? await hookRunner.runBeforePromptBuild(promptEvent, hookCtx).catch((error: unknown) => { log.warn(`before_prompt_build hook failed: ${String(error)}`); return undefined; }) : undefined; const beforeAgentStartResult = hookRunner.hasHooks("before_agent_start") - ? await hookRunner.runBeforeAgentStart(promptEvent, hookCtx).catch((error) => { + ? await hookRunner.runBeforeAgentStart(promptEvent, hookCtx).catch((error: unknown) => { log.warn(`deprecated before_agent_start hook failed during prompt build: ${String(error)}`); return undefined; }) diff --git a/src/agents/main-session-restart-recovery.ts b/src/agents/main-session-restart-recovery.ts index 92479eb6568..7a5f32c2714 100644 --- a/src/agents/main-session-restart-recovery.ts +++ b/src/agents/main-session-restart-recovery.ts @@ -658,7 +658,7 @@ export function scheduleRestartAbortedMainSessionRecovery( attemptRecovery(attempt + 1, delay * RETRY_BACKOFF_MULTIPLIER); } }) - .catch((err) => { + .catch((err: unknown) => { if (attempt < maxRetries) { log.warn(`main-session restart recovery failed: ${String(err)}`); attemptRecovery(attempt + 1, delay * RETRY_BACKOFF_MULTIPLIER); diff --git a/src/agents/model-fallback.ts b/src/agents/model-fallback.ts index b371fec35c4..384a9786620 100644 --- a/src/agents/model-fallback.ts +++ b/src/agents/model-fallback.ts @@ -385,7 +385,7 @@ async function runFallbackAttempt(params: { }); if (classifiedError) { if (isTerminalAbort(params.abortSignal)) { - throw classifiedError; + throw toLintErrorObject(classifiedError, "Non-Error thrown"); } return { error: classifiedError }; } @@ -598,7 +598,7 @@ function throwFallbackFailureSummary(params: { agentDir?: string; }): never { if (params.attempts.length <= 1 && params.lastError) { - throw params.lastError; + throw toLintErrorObject(params.lastError, "Non-Error thrown"); } if (params.attribution?.sessionId) { @@ -1760,3 +1760,17 @@ export async function runWithImageModelFallback(params: { }); } export { testing as __testing }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/model-provider-auth.ts b/src/agents/model-provider-auth.ts index b555f061f7b..9ce729efc9c 100644 --- a/src/agents/model-provider-auth.ts +++ b/src/agents/model-provider-auth.ts @@ -644,7 +644,7 @@ function runProviderAuthWarmWorker(params: { resolve({ agents: [] }); return; } - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }); }); worker.once("exit", (code) => { @@ -700,3 +700,17 @@ export async function warmCurrentProviderAuthStateOffMainThread( } publishProviderAuthWarmSnapshot(snapshot); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/provider-local-service.ts b/src/agents/provider-local-service.ts index fdff061bafc..16440fe819a 100644 --- a/src/agents/provider-local-service.ts +++ b/src/agents/provider-local-service.ts @@ -411,7 +411,7 @@ function waitForAbort(promise: Promise, signal?: AbortSignal | null): Prom }, (error: unknown) => { cleanup(); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); @@ -503,3 +503,17 @@ export function hasLocalServiceProcessExited( ): boolean { return child.exitCode !== null || child.signalCode !== null; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/run-cleanup-timeout.ts b/src/agents/run-cleanup-timeout.ts index aedd594b5c5..22aa4cae106 100644 --- a/src/agents/run-cleanup-timeout.ts +++ b/src/agents/run-cleanup-timeout.ts @@ -88,7 +88,7 @@ export async function runAgentCleanupStep(params: { let timeoutHandle: ReturnType | undefined; let timedOut = false; const cleanupPromise = Promise.resolve().then(params.cleanup); - const observedCleanupPromise = cleanupPromise.catch((error) => { + const observedCleanupPromise = cleanupPromise.catch((error: unknown) => { if (!timedOut) { params.log.warn( `agent cleanup failed: runId=${params.runId} sessionId=${params.sessionId} step=${params.step} error=${formatErrorMessage(error)}`, @@ -114,7 +114,7 @@ export async function runAgentCleanupStep(params: { params.log.warn( `agent cleanup timed out: runId=${params.runId} sessionId=${params.sessionId} step=${params.step} timeoutMs=${timeoutMs}${details}`, ); - void cleanupPromise.catch((error) => { + void cleanupPromise.catch((error: unknown) => { params.log.warn( `agent cleanup rejected after timeout: runId=${params.runId} sessionId=${params.sessionId} step=${params.step} error=${formatErrorMessage(error)}`, ); diff --git a/src/agents/sandbox/docker-backend.ts b/src/agents/sandbox/docker-backend.ts index 38cb2bd9079..09da8720eb9 100644 --- a/src/agents/sandbox/docker-backend.ts +++ b/src/agents/sandbox/docker-backend.ts @@ -22,8 +22,6 @@ function resolveConfiguredDockerRuntimeImage(params: { switch (params.configLabelKind) { case "BrowserImage": return sandboxCfg.browser.image; - case "Image": - case undefined: default: return sandboxCfg.docker.image; } diff --git a/src/agents/sandbox/registry.ts b/src/agents/sandbox/registry.ts index a43052ade02..bc056917f81 100644 --- a/src/agents/sandbox/registry.ts +++ b/src/agents/sandbox/registry.ts @@ -218,7 +218,7 @@ async function readShardedEntries(dir: string): Promise async function quarantineLegacyRegistry(registryPath: string): Promise { const quarantinePath = `${registryPath}.invalid-${Date.now()}`; - await fs.rename(registryPath, quarantinePath).catch(async (error) => { + await fs.rename(registryPath, quarantinePath).catch(async (error: unknown) => { const code = (error as { code?: string } | null)?.code; if (code !== "ENOENT") { await fs.rm(registryPath, { force: true }); diff --git a/src/agents/sandbox/ssh.ts b/src/agents/sandbox/ssh.ts index ba31a307366..1b319d7c5b6 100644 --- a/src/agents/sandbox/ssh.ts +++ b/src/agents/sandbox/ssh.ts @@ -677,7 +677,7 @@ export async function uploadDirectoryToSshTarget(params: { const fail = (error: unknown) => { tar.kill("SIGKILL"); ssh.kill("SIGKILL"); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }; tar.on("error", fail); @@ -778,3 +778,17 @@ async function writeSecretMaterial( await fs.chmod(pathname, 0o600); return pathname; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/sessions/agent-session.ts b/src/agents/sessions/agent-session.ts index cdc859a9694..45524c6a413 100644 --- a/src/agents/sessions/agent-session.ts +++ b/src/agents/sessions/agent-session.ts @@ -2291,7 +2291,7 @@ export class AgentSession { runner.bindCore( { sendMessage: (message, options) => { - this.sendCustomMessage(message, options).catch((err) => { + this.sendCustomMessage(message, options).catch((err: unknown) => { runner.emitError({ extensionPath: "", event: "send_message", @@ -2300,7 +2300,7 @@ export class AgentSession { }); }, sendUserMessage: (content, options) => { - this.sendUserMessage(content, options).catch((err) => { + this.sendUserMessage(content, options).catch((err: unknown) => { runner.emitError({ extensionPath: "", event: "send_user_message", diff --git a/src/agents/sessions/settings-manager.ts b/src/agents/sessions/settings-manager.ts index d2715cea090..58ee349c48b 100644 --- a/src/agents/sessions/settings-manager.ts +++ b/src/agents/sessions/settings-manager.ts @@ -489,7 +489,7 @@ export class SettingsManager { task(); this.clearModifiedScope(scope); }) - .catch((error) => { + .catch((error: unknown) => { this.recordError(scope, error); }); } diff --git a/src/agents/sessions/tools/bash.ts b/src/agents/sessions/tools/bash.ts index 575f90210b8..fc2033e3c6a 100644 --- a/src/agents/sessions/tools/bash.ts +++ b/src/agents/sessions/tools/bash.ts @@ -109,14 +109,14 @@ export function createLocalBashOperations(options?: { shellPath?: string }): Bas } resolve({ exitCode: code }); }) - .catch((err) => { + .catch((err: unknown) => { if (timeoutHandle) { clearTimeout(timeoutHandle); } if (signal) { signal.removeEventListener("abort", onAbort); } - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }); }); }, @@ -468,3 +468,17 @@ export function createBashTool( ): AgentTool { return wrapToolDefinition(createBashToolDefinition(cwd, options)); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/sessions/tools/ls.ts b/src/agents/sessions/tools/ls.ts index ade5a467d54..053b298066f 100644 --- a/src/agents/sessions/tools/ls.ts +++ b/src/agents/sessions/tools/ls.ts @@ -223,7 +223,7 @@ export function createLsToolDefinition( }); } catch (e: unknown) { signal?.removeEventListener("abort", onAbort); - reject(e); + reject(toLintErrorObject(e, "Non-Error rejection")); } })(); }); @@ -244,3 +244,17 @@ export function createLsToolDefinition( export function createLsTool(cwd: string, options?: LsToolOptions): AgentTool { return wrapToolDefinition(createLsToolDefinition(cwd, options)); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/sessions/tools/read.ts b/src/agents/sessions/tools/read.ts index 231177a7dd5..286027be4fd 100644 --- a/src/agents/sessions/tools/read.ts +++ b/src/agents/sessions/tools/read.ts @@ -375,7 +375,7 @@ export function createReadToolDefinition( } catch (error: unknown) { signal?.removeEventListener("abort", onAbort); if (!aborted) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); } } })(); @@ -417,3 +417,17 @@ export function createReadTool( ): AgentTool { return wrapToolDefinition(createReadToolDefinition(cwd, options)); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/subagent-announce.live.test.ts b/src/agents/subagent-announce.live.test.ts index 5c8ceffe2e6..fe71385516d 100644 --- a/src/agents/subagent-announce.live.test.ts +++ b/src/agents/subagent-announce.live.test.ts @@ -352,7 +352,7 @@ describeLive("subagent announce live", () => { const completedRunBeforeDelivery = await waitFor("issue 82913 child completion", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } return listSubagentRunsForRequester(sessionKey).find( (run) => @@ -541,7 +541,7 @@ describeLive("subagent announce live", () => { listSubagentRunsForRequester(sessionKey).filter((run) => run.taskName === "steered_child"); const spawnedRun = await waitFor("steered child spawn", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } return listSteeredChildRuns()[0]; }); @@ -550,7 +550,7 @@ describeLive("subagent announce live", () => { expect(extractPayloadText(initialResponse.result)).toContain(parentStartedToken); const runBeforeSteer = await waitFor("steered child bash tool start", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } const currentRun = listSteeredChildRuns().find((run) => run.runId === spawnedRun.runId) ?? spawnedRun; @@ -592,7 +592,7 @@ describeLive("subagent announce live", () => { const steeredRun = await waitFor("steered child completion", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } return listSteeredChildRuns().find( (run) => @@ -614,7 +614,7 @@ describeLive("subagent announce live", () => { await waitFor("in-process subagent completion agent dispatch start", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } return inProcessAgentDispatches.some((entry) => entry.phase === "started") ? true @@ -625,7 +625,7 @@ describeLive("subagent announce live", () => { "in-process subagent completion agent dispatch", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } return inProcessAgentDispatches.find((entry) => entry.phase === "completed"); }, @@ -634,7 +634,7 @@ describeLive("subagent announce live", () => { expect( inProcessAgentDispatches.some((entry) => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } return entry.phase === "started"; }), @@ -761,7 +761,7 @@ describeLive("subagent announce live", () => { const completedRuns = await waitFor("three Gemini stress child completions", () => { if (initialError) { - throw initialError; + throw toLintErrorObject(initialError, "Non-Error thrown"); } const runs = listSubagentRunsForRequester(sessionKey).filter((run) => run.taskName?.startsWith("gemini_stress_"), @@ -789,3 +789,17 @@ describeLive("subagent announce live", () => { 12 * 60_000, ); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/subagent-orphan-recovery.ts b/src/agents/subagent-orphan-recovery.ts index 619f2771b9f..f85f8e8ebf0 100644 --- a/src/agents/subagent-orphan-recovery.ts +++ b/src/agents/subagent-orphan-recovery.ts @@ -456,7 +456,7 @@ export function scheduleOrphanRecovery(params: { ), ); }) - .catch((err) => { + .catch((err: unknown) => { if (attempt < maxRetries) { const nextDelay = delay * RETRY_BACKOFF_MULTIPLIER; log.warn( diff --git a/src/agents/subagent-registry-lifecycle.ts b/src/agents/subagent-registry-lifecycle.ts index b675d1e17cb..878ee7c712b 100644 --- a/src/agents/subagent-registry-lifecycle.ts +++ b/src/agents/subagent-registry-lifecycle.ts @@ -704,7 +704,7 @@ export function createSubagentRegistryLifecycleController(params: { runId, entry, reason: "expiry", - }).catch((error) => { + }).catch((error: unknown) => { defaultRuntime.log( `[warn] Subagent expiry finalize failed during deferred retry for run ${runId}: ${String(error)}`, ); @@ -965,7 +965,7 @@ export function createSubagentRegistryLifecycleController(params: { } void finalizeSubagentCleanup(runId, entry.cleanup, true, { skipAnnounce: true, - }).catch((err) => { + }).catch((err: unknown) => { defaultRuntime.log(`[warn] subagent cleanup finalize failed (${runId}): ${String(err)}`); const current = params.runs.get(runId); if (!current || current.cleanupCompletedAt) { @@ -998,7 +998,7 @@ export function createSubagentRegistryLifecycleController(params: { skipAnnounce: true, skipDeliveryStatus: true, }); - })().catch((err) => { + })().catch((err: unknown) => { defaultRuntime.log(`[warn] subagent cleanup finalize failed (${runId}): ${String(err)}`); const current = params.runs.get(runId); if (!current || current.cleanupCompletedAt) { @@ -1025,7 +1025,7 @@ export function createSubagentRegistryLifecycleController(params: { runId, entry.cleanup, didAnnounce || shouldCreditPriorDelivery, - ).catch((err) => { + ).catch((err: unknown) => { defaultRuntime.log(`[warn] subagent cleanup finalize failed (${runId}): ${String(err)}`); const current = params.runs.get(runId); if (!current || current.cleanupCompletedAt) { @@ -1080,7 +1080,7 @@ export function createSubagentRegistryLifecycleController(params: { .then((didAnnounce) => { void finalizeAnnounceCleanup(didAnnounce); }) - .catch((error) => { + .catch((error: unknown) => { defaultRuntime.log( `[warn] Subagent announce flow failed during cleanup for run ${runId}: ${String(error)}`, ); diff --git a/src/agents/subagent-registry-run-manager.ts b/src/agents/subagent-registry-run-manager.ts index 48b7dab7c95..f22da824fdd 100644 --- a/src/agents/subagent-registry-run-manager.ts +++ b/src/agents/subagent-registry-run-manager.ts @@ -794,7 +794,7 @@ export function createSubagentRunManager(params: { inFlightRunIds: params.endedHookInFlightRunIds, persist: () => params.persist(), }); - void persistSubagentSessionTiming(entry).catch((err) => { + void persistSubagentSessionTiming(entry).catch((err: unknown) => { log.warn("failed to persist killed subagent session timing", { err, runId: entry.runId, diff --git a/src/agents/subagent-registry.ts b/src/agents/subagent-registry.ts index 45d83a01fc8..d9c5a9ff706 100644 --- a/src/agents/subagent-registry.ts +++ b/src/agents/subagent-registry.ts @@ -1173,7 +1173,7 @@ function ensureListener() { startedAt, }; await completeSubagentRunWithRecovery(completionParams, "lifecycle-ok-event"); - })().catch((err) => { + })().catch((err: unknown) => { log.warn("lifecycle event handler failed", { err, runId: evt.runId }); }); }); diff --git a/src/agents/tool-images.ts b/src/agents/tool-images.ts index 5e97a1ea55b..4a31b8e420d 100644 --- a/src/agents/tool-images.ts +++ b/src/agents/tool-images.ts @@ -241,7 +241,7 @@ async function resizeImageBase64IfNeeded(params: { } if (processorUnavailableError) { - throw processorUnavailableError; + throw toLintErrorObject(processorUnavailableError, "Non-Error thrown"); } const best = smallest?.buffer ?? buf; @@ -356,3 +356,17 @@ export async function sanitizeToolResultImages( const next = await sanitizeContentBlocksImages(content, label, opts); return { ...result, content: next }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/agents/tools/media-generate-background-shared.ts b/src/agents/tools/media-generate-background-shared.ts index f1364351cde..12b2229d318 100644 --- a/src/agents/tools/media-generate-background-shared.ts +++ b/src/agents/tools/media-generate-background-shared.ts @@ -297,7 +297,7 @@ export function createDefaultMediaGenerateBackgroundScheduler(params: { }): MediaGenerateBackgroundScheduler { return (work) => { queueMicrotask(() => { - void work().catch((error) => { + void work().catch((error: unknown) => { params.onCrash(`Detached ${params.toolName} job crashed`, { error }); }); }); diff --git a/src/agents/tools/transcripts-tool.ts b/src/agents/tools/transcripts-tool.ts index cf81bbbbacf..05df38bcdc5 100644 --- a/src/agents/tools/transcripts-tool.ts +++ b/src/agents/tools/transcripts-tool.ts @@ -480,7 +480,7 @@ export function createTranscriptsAutoStartService(ctx: TranscriptsRuntimeContext startedSessionIds.add(sessionId); } }) - .catch((err) => { + .catch((err: unknown) => { if (stopped) { return; } @@ -542,7 +542,7 @@ export function createTranscriptsAutoStartService(ctx: TranscriptsRuntimeContext ctx, store, rawParams: { action: "stop", sessionId }, - }).catch((err) => + }).catch((err: unknown) => ctx.logger.warn( `transcripts autoStart stop failed session=${sessionId}: ${ err instanceof Error ? err.message : String(err) diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index c1336534734..320c1320c47 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -2580,7 +2580,7 @@ export async function runAgentTurnWithFallback(params: { text, }); }) - .catch((err) => { + .catch((err: unknown) => { // Keep chain healthy after an error so later tool results still deliver. logVerbose(`tool result delivery failed: ${String(err)}`); }); diff --git a/src/auto-reply/reply/block-reply-pipeline.ts b/src/auto-reply/reply/block-reply-pipeline.ts index c6b8b2c0f99..d8c0a222ed8 100644 --- a/src/auto-reply/reply/block-reply-pipeline.ts +++ b/src/auto-reply/reply/block-reply-pipeline.ts @@ -177,7 +177,7 @@ export function createBlockReplyPipeline(params: { didStream = true; } }) - .catch((err) => { + .catch((err: unknown) => { if (err === timeoutError) { abortController.abort(); aborted = true; diff --git a/src/auto-reply/reply/dispatch-acp-delivery.ts b/src/auto-reply/reply/dispatch-acp-delivery.ts index d59f85414ad..ec5e41e498b 100644 --- a/src/auto-reply/reply/dispatch-acp-delivery.ts +++ b/src/auto-reply/reply/dispatch-acp-delivery.ts @@ -273,7 +273,7 @@ export function createAcpDispatchDeliveryCoordinator(params: { if (params.suppressReplyLifecycle) { return; } - void Promise.resolve(params.onReplyStart?.()).catch((error) => { + void Promise.resolve(params.onReplyStart?.()).catch((error: unknown) => { logVerbose( `dispatch-acp: reply lifecycle start failed: ${error instanceof Error ? error.message : String(error)}`, ); diff --git a/src/auto-reply/reply/followup-runner.ts b/src/auto-reply/reply/followup-runner.ts index e6197d16005..d3a149f2924 100644 --- a/src/auto-reply/reply/followup-runner.ts +++ b/src/auto-reply/reply/followup-runner.ts @@ -490,7 +490,7 @@ export function createFollowupRunner(params: { let progressDeliveryChain: Promise = Promise.resolve(); const pendingProgressDeliveries = new Set>(); const enqueueProgressDelivery = (deliver: () => Promise) => { - progressDeliveryChain = progressDeliveryChain.then(deliver).catch((err) => { + progressDeliveryChain = progressDeliveryChain.then(deliver).catch((err: unknown) => { logVerbose(`followup queue: progress delivery failed: ${formatErrorMessage(err)}`); }); const task = progressDeliveryChain.finally(() => { diff --git a/src/auto-reply/reply/reply-delivery.ts b/src/auto-reply/reply/reply-delivery.ts index 218aa25349e..4de0e896d0c 100644 --- a/src/auto-reply/reply/reply-delivery.ts +++ b/src/auto-reply/reply/reply-delivery.ts @@ -146,7 +146,7 @@ export function createBlockReplyDeliveryHandler(params: { } if (blockPayload.text) { - void params.typingSignals.signalTextDelta(blockPayload.text).catch((err) => { + void params.typingSignals.signalTextDelta(blockPayload.text).catch((err: unknown) => { logVerbose(`block reply typing signal failed: ${String(err)}`); }); } diff --git a/src/auto-reply/reply/reply-dispatcher.ts b/src/auto-reply/reply/reply-dispatcher.ts index ecb01121220..3e1e443e878 100644 --- a/src/auto-reply/reply/reply-dispatcher.ts +++ b/src/auto-reply/reply/reply-dispatcher.ts @@ -207,7 +207,7 @@ export function createReplyDispatcher(options: ReplyDispatcherOptions): ReplyDis } await options.deliver(deliverPayload, { kind }); }) - .catch((err) => { + .catch((err: unknown) => { failedCounts[kind] += 1; void options.onError?.(err, { kind }); }) diff --git a/src/auto-reply/reply/reply-media-paths.ts b/src/auto-reply/reply/reply-media-paths.ts index 4799ac95227..aa4a28d7116 100644 --- a/src/auto-reply/reply/reply-media-paths.ts +++ b/src/auto-reply/reply/reply-media-paths.ts @@ -129,7 +129,7 @@ export function createReplyMediaPathNormalizer(params: { mediaAccess: resolveMediaAccessForSource(media), }) .then((saved) => saved.path) - .catch((err) => { + .catch((err: unknown) => { persistedMediaBySource.delete(media); throw err; }); diff --git a/src/auto-reply/reply/session-updates.ts b/src/auto-reply/reply/session-updates.ts index 900a8e28cad..4cc0e6c6b43 100644 --- a/src/auto-reply/reply/session-updates.ts +++ b/src/auto-reply/reply/session-updates.ts @@ -97,7 +97,7 @@ function emitCompactionSessionLifecycleHooks(params: { transcriptArchived: transcript.transcriptArchived, nextSessionId: params.nextEntry.sessionId, }); - void hookRunner.runSessionEnd(payload.event, payload.context).catch((err) => { + void hookRunner.runSessionEnd(payload.event, payload.context).catch((err: unknown) => { logVerbose(`session_end hook failed: ${String(err)}`); }); } @@ -109,7 +109,7 @@ function emitCompactionSessionLifecycleHooks(params: { cfg: params.cfg, resumedFrom: params.previousEntry.sessionId, }); - void hookRunner.runSessionStart(payload.event, payload.context).catch((err) => { + void hookRunner.runSessionStart(payload.event, payload.context).catch((err: unknown) => { logVerbose(`session_start hook failed: ${String(err)}`); }); } diff --git a/src/channels/ack-reactions.ts b/src/channels/ack-reactions.ts index 5cc59fe0b09..f4d3693978c 100644 --- a/src/channels/ack-reactions.ts +++ b/src/channels/ack-reactions.ts @@ -99,13 +99,13 @@ export function createAckReactionHandle(params: { try { sendPromise = params.send(); } catch (err) { - sendPromise = Promise.reject(err); + sendPromise = Promise.reject(toLintErrorObject(err, "Non-Error rejection")); } return { ackReactionPromise: sendPromise.then( () => true, - (err) => { + (err: unknown) => { params.onSendError?.(err); return false; }, @@ -135,7 +135,7 @@ export function removeAckReactionAfterReply(params: { if (!didAck) { return; } - params.remove().catch((err) => params.onError?.(err)); + params.remove().catch((err: unknown) => params.onError?.(err)); }); } @@ -152,3 +152,17 @@ export function removeAckReactionHandleAfterReply(params: { onError: params.onError, }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/channels/plugins/binding-routing.ts b/src/channels/plugins/binding-routing.ts index e6b9add09e3..0fc89ae6dc0 100644 --- a/src/channels/plugins/binding-routing.ts +++ b/src/channels/plugins/binding-routing.ts @@ -189,7 +189,7 @@ export async function ensureConfiguredBindingRouteReady(params: { logVerbose( `configured binding route ready check settled after timeout (ok=${lateResult.ok})`, ), - (err) => + (err: unknown) => logVerbose(`configured binding route ready check rejected after timeout: ${String(err)}`), ); return { ok: false, error: "Configured binding route ready check timed out" }; diff --git a/src/channels/typing.ts b/src/channels/typing.ts index 4ca7aff5d2f..0c23c4543b0 100644 --- a/src/channels/typing.ts +++ b/src/channels/typing.ts @@ -113,7 +113,7 @@ export function createTypingCallbacks(params: CreateTypingCallbacksParams): Typi return; } stopSent = true; - void stop().catch((err) => (params.onStopError ?? params.onStartError)(err)); + void stop().catch((err: unknown) => (params.onStopError ?? params.onStartError)(err)); }; return { onReplyStart, onIdle: fireStop, onCleanup: fireStop }; diff --git a/src/cli/daemon-cli/status.gather.ts b/src/cli/daemon-cli/status.gather.ts index 675bf44c90f..ee12d78818b 100644 --- a/src/cli/daemon-cli/status.gather.ts +++ b/src/cli/daemon-cli/status.gather.ts @@ -505,7 +505,9 @@ export async function gatherDaemonStatus( : process.env; const [loaded, runtime] = await Promise.all([ service.isLoaded({ env: serviceEnv }).catch(() => false), - service.readRuntime(serviceEnv).catch((err) => ({ status: "unknown", detail: String(err) })), + service + .readRuntime(serviceEnv) + .catch((err: unknown) => ({ status: "unknown", detail: String(err) })), ]); const restartHandoff = opts.deep ? readGatewayRestartHandoffSync(serviceEnv) : null; const configAudit = command diff --git a/src/cli/gateway-cli/qa-parent-watchdog.ts b/src/cli/gateway-cli/qa-parent-watchdog.ts index 85e14dd8ae7..53e517a8fec 100644 --- a/src/cli/gateway-cli/qa-parent-watchdog.ts +++ b/src/cli/gateway-cli/qa-parent-watchdog.ts @@ -146,7 +146,7 @@ export function installQaParentWatchdog( } } for (const cleanupRoot of qaCleanupRoots) { - await rm(cleanupRoot).catch((cleanupError) => { + await rm(cleanupRoot).catch((cleanupError: unknown) => { logger.warn( `QA gateway parent pid ${parentPid} exited; failed to clean runtime root ${cleanupRoot}: ${ cleanupError instanceof Error ? cleanupError.message : String(cleanupError) diff --git a/src/cli/gateway-cli/run-loop.ts b/src/cli/gateway-cli/run-loop.ts index 532b9c85411..a6b74c116dd 100644 --- a/src/cli/gateway-cli/run-loop.ts +++ b/src/cli/gateway-cli/run-loop.ts @@ -206,7 +206,7 @@ export async function runGatewayLoop(params: { } catch { // Best-effort; parent fallback keeps the gateway reachable for recovery. } - await markUpdateRestartSentinelFailure("restart-unhealthy").catch((err) => { + await markUpdateRestartSentinelFailure("restart-unhealthy").catch((err: unknown) => { gatewayLog.warn(`failed to mark update restart sentinel unhealthy: ${String(err)}`); }); if (hadLock && !(await reacquireLockForInProcessRestart())) { @@ -243,7 +243,7 @@ export async function runGatewayLoop(params: { gatewayLog.warn( `update respawn failed (${respawn.detail ?? "unknown error"}); falling back to in-process restart`, ); - await markUpdateRestartSentinelFailure("restart-unhealthy").catch((err) => { + await markUpdateRestartSentinelFailure("restart-unhealthy").catch((err: unknown) => { gatewayLog.warn(`failed to mark update restart sentinel unhealthy: ${String(err)}`); }); } else { @@ -371,7 +371,7 @@ export async function runGatewayLoop(params: { restartDrainingMarkPromise = (async () => { const { markGatewayDraining } = await loadGatewayLifecycleRuntimeModule(); markGatewayDraining(); - })().catch((err) => { + })().catch((err: unknown) => { restartDrainingMarkPromise = null; throw err; }); @@ -687,7 +687,7 @@ export async function runGatewayLoop(params: { } if (!server || !restartResolver) { pendingStartupRequest = acceptedRequest; - void markRestartDraining().catch((err) => { + void markRestartDraining().catch((err: unknown) => { gatewayLog.warn(`failed to mark gateway draining for startup restart: ${String(err)}`); }); armPendingStartupForceExitTimer(); @@ -768,7 +768,7 @@ export async function runGatewayLoop(params: { sigusr1RestartIntent?.reason ?? restartReason, sigusr1RestartIntent ?? undefined, ); - })().catch((err) => { + })().catch((err: unknown) => { // Defense in depth: if anything in the listener body rejects, the // SIGUSR1 emit has already advanced emittedRestartToken but no one // called markGatewaySigusr1RestartHandled. Without unsticking the diff --git a/src/cli/gateway-cli/run.ts b/src/cli/gateway-cli/run.ts index 6fdfa1951f8..1f00e795820 100644 --- a/src/cli/gateway-cli/run.ts +++ b/src/cli/gateway-cli/run.ts @@ -541,7 +541,7 @@ export async function runGatewayCommand(opts: GatewayRunOpts) { const { cfg, snapshot, startupConfigSnapshotRead } = await readGatewayStartupConfig({ startupTrace, }); - void maybeLogPendingControlUiBuild(cfg).catch((err) => { + void maybeLogPendingControlUiBuild(cfg).catch((err: unknown) => { gatewayLog.warn(`Control UI asset check failed: ${String(err)}`); }); const portOverride = parsePort(opts.port); diff --git a/src/cli/node-cli/daemon.ts b/src/cli/node-cli/daemon.ts index 4359ec6ff21..5b52bc225d2 100644 --- a/src/cli/node-cli/daemon.ts +++ b/src/cli/node-cli/daemon.ts @@ -219,7 +219,7 @@ export async function runNodeDaemonStatus(opts: NodeDaemonStatusOptions = {}) { service.readCommand(process.env).catch(() => null), service .readRuntime(process.env) - .catch((err): GatewayServiceRuntime => ({ status: "unknown", detail: String(err) })), + .catch((err: unknown): GatewayServiceRuntime => ({ status: "unknown", detail: String(err) })), ]); const payload = { diff --git a/src/cli/nodes-camera.ts b/src/cli/nodes-camera.ts index cbb849ea6f4..720cdcc8b78 100644 --- a/src/cli/nodes-camera.ts +++ b/src/cli/nodes-camera.ts @@ -169,7 +169,7 @@ export async function writeUrlToFile( if (thrown) { await fs.unlink(filePath).catch(() => {}); - throw thrown; + throw toLintErrorObject(thrown, "Non-Error thrown"); } } finally { await release(); @@ -250,3 +250,17 @@ export async function writeCameraClipPayloadToFile(params: { }); return filePath; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/cli/pairing-cli.ts b/src/cli/pairing-cli.ts index 60462ca8115..b2c12ca6560 100644 --- a/src/cli/pairing-cli.ts +++ b/src/cli/pairing-cli.ts @@ -214,7 +214,7 @@ export function registerPairingCli(program: Command) { } const approvedAccountId = accountId || normalizeStringifiedOptionalString(approved.entry?.meta?.accountId); - await notifyApproved(channel, approved.id, approvedAccountId).catch((err) => { + await notifyApproved(channel, approved.id, approvedAccountId).catch((err: unknown) => { defaultRuntime.log(theme.warn(`Failed to notify requester: ${String(err)}`)); }); }); diff --git a/src/cli/program/build-program.test.ts b/src/cli/program/build-program.test.ts index ee472b02b55..bc2887d6d44 100644 --- a/src/cli/program/build-program.test.ts +++ b/src/cli/program/build-program.test.ts @@ -41,7 +41,7 @@ describe("buildProgram", () => { } async function expectCommanderExit(promise: Promise, exitCode: number) { - const error = await promise.catch((err) => err); + const error = await promise.catch((err: unknown) => err); expect(error).toBeInstanceOf(CommanderError); expect((error as CommanderError).exitCode).toBe(exitCode); diff --git a/src/cli/prompt.ts b/src/cli/prompt.ts index b314265df42..14fbf8255fa 100644 --- a/src/cli/prompt.ts +++ b/src/cli/prompt.ts @@ -28,7 +28,7 @@ function questionUntilClose(rl: ReadlineInterface, question: string): Promise finish(() => resolve(answer)), - (error: unknown) => finish(() => reject(error)), + (error: unknown) => finish(() => reject(toLintErrorObject(error, "Non-Error rejection"))), ); }); } @@ -53,3 +53,17 @@ export async function promptYesNo(question: string, defaultYes = false): Promise } return answer.startsWith("y"); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/cli/security-cli.ts b/src/cli/security-cli.ts index e3f4fd0eeb9..82b46f2c45c 100644 --- a/src/cli/security-cli.ts +++ b/src/cli/security-cli.ts @@ -119,7 +119,9 @@ export function registerSecurityCli(program: Command) { token, password, }); - const fixResult = opts.fix ? await fixSecurityFootguns().catch((_err) => null) : null; + const fixResult = opts.fix + ? await fixSecurityFootguns().catch((_err: unknown) => null) + : null; const sourceConfig = getRuntimeConfig(); const { resolvedConfig: cfg, diagnostics: secretDiagnostics } = diff --git a/src/commands/chutes-oauth.ts b/src/commands/chutes-oauth.ts index d7b86d1806b..94f20404787 100644 --- a/src/commands/chutes-oauth.ts +++ b/src/commands/chutes-oauth.ts @@ -130,7 +130,7 @@ async function waitForLocalCallback(params: { clearTimeout(timeout); } server.close(); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } }); @@ -216,3 +216,17 @@ export async function loginChutes(params: { fetchFn: params.fetchFn, }); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/commands/configure.daemon.ts b/src/commands/configure.daemon.ts index a6038ee1074..e170f0b46ca 100644 --- a/src/commands/configure.daemon.ts +++ b/src/commands/configure.daemon.ts @@ -143,7 +143,7 @@ export async function maybeInstallDaemon(params: { }, ); if (installError) { - note("Gateway service install failed: " + installError, "Gateway"); + note("Gateway service install failed: ".concat(installError), "Gateway"); note(gatewayInstallErrorHint(), "Gateway"); return; } diff --git a/src/commands/models.list.e2e.test.ts b/src/commands/models.list.e2e.test.ts index 67e8dec51f3..b19b67e6040 100644 --- a/src/commands/models.list.e2e.test.ts +++ b/src/commands/models.list.e2e.test.ts @@ -102,7 +102,7 @@ vi.mock("../agents/agent-model-discovery.js", () => { class MockModelRegistry { find(provider: string, id: string) { if (modelRegistryState.findError !== undefined) { - throw modelRegistryState.findError; + throw toLintErrorObject(modelRegistryState.findError, "Non-Error thrown"); } return ( modelRegistryState.models.find((model) => model.provider === provider && model.id === id) ?? @@ -112,14 +112,14 @@ vi.mock("../agents/agent-model-discovery.js", () => { getAll() { if (modelRegistryState.getAllError !== undefined) { - throw modelRegistryState.getAllError; + throw toLintErrorObject(modelRegistryState.getAllError, "Non-Error thrown"); } return modelRegistryState.models; } getAvailable() { if (modelRegistryState.getAvailableError !== undefined) { - throw modelRegistryState.getAvailableError; + throw toLintErrorObject(modelRegistryState.getAvailableError, "Non-Error thrown"); } return modelRegistryState.available; } @@ -768,3 +768,17 @@ describe("models list/status", () => { expect(row.available).toBe(false); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/commands/status-runtime-shared.ts b/src/commands/status-runtime-shared.ts index fd8955d2f9e..727b32520fa 100644 --- a/src/commands/status-runtime-shared.ts +++ b/src/commands/status-runtime-shared.ts @@ -108,7 +108,7 @@ export async function resolveStatusGatewayHealthSafe(params: { timeoutMs: params.timeoutMs, config: params.config, ...params.callOverrides, - }).catch((err) => ({ error: String(err) })); + }).catch((err: unknown) => ({ error: String(err) })); } export async function resolveStatusGatewayDiagnosticsSafe(params: { diff --git a/src/commitments/runtime.ts b/src/commitments/runtime.ts index 05c5918d867..0ea8f429fa2 100644 --- a/src/commitments/runtime.ts +++ b/src/commitments/runtime.ts @@ -147,7 +147,7 @@ export function enqueueCommitmentExtraction(input: CommitmentExtractionEnqueueIn if (!timer) { timer = setTimer(() => { timer = null; - void drainCommitmentExtractionQueue().catch((err) => { + void drainCommitmentExtractionQueue().catch((err: unknown) => { log.warn("commitment extraction failed", { error: String(err) }); }); }, resolved.extraction.debounceMs); diff --git a/src/cron/service/failure-alerts.ts b/src/cron/service/failure-alerts.ts index b32ff3bc1e3..1550e7cdb68 100644 --- a/src/cron/service/failure-alerts.ts +++ b/src/cron/service/failure-alerts.ts @@ -131,7 +131,7 @@ function emitFailureAlert( mode: params.mode, accountId: params.accountId, }) - .catch((err) => { + .catch((err: unknown) => { state.deps.log.warn( { jobId: params.job.id, err: String(err) }, "cron: failure alert delivery failed", diff --git a/src/cron/service/ops.ts b/src/cron/service/ops.ts index b5670fbcf2c..60c773309e2 100644 --- a/src/cron/service/ops.ts +++ b/src/cron/service/ops.ts @@ -928,7 +928,7 @@ export async function enqueueRun(state: CronServiceState, id: string, mode?: "du ); }, }, - ).catch((err) => { + ).catch((err: unknown) => { state.deps.log.error( { jobId: id, runId, err: String(err) }, "cron: queued manual run background execution failed", diff --git a/src/cron/service/timer.ts b/src/cron/service/timer.ts index de9dc9f539e..b4dbe526c62 100644 --- a/src/cron/service/timer.ts +++ b/src/cron/service/timer.ts @@ -152,7 +152,7 @@ export async function executeJobCoreWithTimeout( onExecutionPhase: deferTimeoutUntilExecutionStart ? watchdog.notePhase : undefined, }); watchdog.start(); - void corePromise.catch((err) => { + void corePromise.catch((err: unknown) => { if (runAbortController.signal.aborted) { state.deps.log.warn( { jobId: job.id, err: String(err) }, @@ -751,7 +751,7 @@ export function armTimer(state: CronServiceState) { // Vitest's fake-timer helpers can await async callbacks, which would block // tests that simulate long-running jobs. Runtime behavior is unchanged. state.timer = setTimeout(() => { - void onTimer(state).catch((err) => { + void onTimer(state).catch((err: unknown) => { state.deps.log.error({ err: String(err) }, "cron: timer tick failed"); }); }, clampedDelay); @@ -766,7 +766,7 @@ function armRunningRecheckTimer(state: CronServiceState) { clearTimeout(state.timer); } state.timer = setTimeout(() => { - void onTimer(state).catch((err) => { + void onTimer(state).catch((err: unknown) => { state.deps.log.error({ err: String(err) }, "cron: timer tick failed"); }); }, MAX_TIMER_DELAY_MS); diff --git a/src/gateway/call.test.ts b/src/gateway/call.test.ts index 28e1a5448f6..e0b8a8de49e 100644 --- a/src/gateway/call.test.ts +++ b/src/gateway/call.test.ts @@ -1130,7 +1130,7 @@ describe("callGateway error details", () => { setLocalLoopbackGatewayConfig(); let err: unknown; - await callGateway({ method: "health", timeoutMs: 10_000 }).catch((caught) => { + await callGateway({ method: "health", timeoutMs: 10_000 }).catch((caught: unknown) => { err = caught; }); @@ -1145,7 +1145,7 @@ describe("callGateway error details", () => { vi.useFakeTimers(); let errMessage = ""; - const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught) => { + const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught: unknown) => { errMessage = caught instanceof Error ? caught.message : String(caught); }); @@ -1164,7 +1164,7 @@ describe("callGateway error details", () => { vi.useFakeTimers(); let err: unknown; - const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught) => { + const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught: unknown) => { err = caught; }); @@ -1185,7 +1185,7 @@ describe("callGateway error details", () => { setLocalLoopbackGatewayConfig(); let err: unknown; - await callGateway({ method: "health" }).catch((caught) => { + await callGateway({ method: "health" }).catch((caught: unknown) => { err = caught; }); @@ -1213,7 +1213,7 @@ describe("callGateway error details", () => { vi.useFakeTimers(); let errMessage = ""; - const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught) => { + const promise = callGateway({ method: "health", timeoutMs: 5 }).catch((caught: unknown) => { errMessage = caught instanceof Error ? caught.message : String(caught); }); @@ -1241,7 +1241,7 @@ describe("callGateway error details", () => { }; let err: unknown; - await callGateway({ method: "health", timeoutMs: 5 }).catch((caught) => { + await callGateway({ method: "health", timeoutMs: 5 }).catch((caught: unknown) => { err = caught; }); expect(isGatewayTransportError(err)).toBe(true); @@ -1264,7 +1264,7 @@ describe("callGateway error details", () => { vi.useFakeTimers(); let errMessage = ""; - const promise = callGateway({ method: "health" }).catch((caught) => { + const promise = callGateway({ method: "health" }).catch((caught: unknown) => { errMessage = caught instanceof Error ? caught.message : String(caught); }); @@ -1285,7 +1285,7 @@ describe("callGateway error details", () => { vi.useFakeTimers(); let errMessage = ""; - const promise = callGateway({ method: "health" }).catch((caught) => { + const promise = callGateway({ method: "health" }).catch((caught: unknown) => { errMessage = caught instanceof Error ? caught.message : String(caught); }); @@ -1306,9 +1306,11 @@ describe("callGateway error details", () => { vi.useFakeTimers(); let errMessage = ""; - const promise = callGateway({ method: "health", timeoutMs: 2_592_010_000 }).catch((caught) => { - errMessage = caught instanceof Error ? caught.message : String(caught); - }); + const promise = callGateway({ method: "health", timeoutMs: 2_592_010_000 }).catch( + (caught: unknown) => { + errMessage = caught instanceof Error ? caught.message : String(caught); + }, + ); await vi.advanceTimersByTimeAsync(1); expect(errMessage).toBe(""); diff --git a/src/gateway/call.ts b/src/gateway/call.ts index 2abf5887ec7..454d1624def 100644 --- a/src/gateway/call.ts +++ b/src/gateway/call.ts @@ -982,7 +982,7 @@ async function executeGatewayRequestWithScopes(params: { }), ); }) - .catch((err) => { + .catch((err: unknown) => { if (settled) { return; } diff --git a/src/gateway/exec-approval-ios-push.ts b/src/gateway/exec-approval-ios-push.ts index 21e53189dc6..d4cd9ff782c 100644 --- a/src/gateway/exec-approval-ios-push.ts +++ b/src/gateway/exec-approval-ios-push.ts @@ -309,7 +309,7 @@ export function createExecApprovalIosPushDelivery(params: { log: GatewayLikeLogg const deliveryState: ApprovalDeliveryState = { nodeIds: plan.targets.map((target) => target.nodeId), requestPushPromise: sendRequestedPushes({ request, plan, log: params.log }).catch( - (err) => { + (err: unknown) => { const message = formatErrorMessage(err); params.log.error?.(`exec approvals: iOS request push failed: ${message}`); return { attempted: plan.targets.length, delivered: 0 }; diff --git a/src/gateway/gateway-acp-bind.live.test.ts b/src/gateway/gateway-acp-bind.live.test.ts index c95eb8eb62e..8fba3b138d5 100644 --- a/src/gateway/gateway-acp-bind.live.test.ts +++ b/src/gateway/gateway-acp-bind.live.test.ts @@ -370,7 +370,7 @@ async function bindConversationAndWait(params: { logLiveStep(`acpx backend became healthy before bind attempt ${attempt}`); } else { if (runtime?.doctor && (attempt === 1 || attempt % 6 === 0)) { - const report = await runtime.doctor().catch((error) => ({ + const report = await runtime.doctor().catch((error: unknown) => ({ message: error instanceof Error ? error.message : String(error), details: [], })); diff --git a/src/gateway/gateway-acp-spawn-defaults.live.test.ts b/src/gateway/gateway-acp-spawn-defaults.live.test.ts index f22463b5a3a..7997de65184 100644 --- a/src/gateway/gateway-acp-spawn-defaults.live.test.ts +++ b/src/gateway/gateway-acp-spawn-defaults.live.test.ts @@ -76,7 +76,7 @@ async function prepareCodexHomeForLiveSpawnDefaultsTest(tempRoot: string): Promi if (sourceCodexHome) { await fs .copyFile(path.join(sourceCodexHome, "auth.json"), path.join(codexHome, "auth.json")) - .catch((error) => { + .catch((error: unknown) => { if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") { throw error; } diff --git a/src/gateway/gateway-cli-backend.live-helpers.ts b/src/gateway/gateway-cli-backend.live-helpers.ts index bed27ef2d58..7510142a530 100644 --- a/src/gateway/gateway-cli-backend.live-helpers.ts +++ b/src/gateway/gateway-cli-backend.live-helpers.ts @@ -422,7 +422,7 @@ async function connectClientOnce(params: { finish({ error: new Error("gateway event loop readiness timeout") }); } }, - (error) => { + (error: unknown) => { finish({ error: error instanceof Error ? error : new Error(String(error)) }); }, ); diff --git a/src/gateway/model-pricing-cache.test.ts b/src/gateway/model-pricing-cache.test.ts index a3fbe7ff02d..a9519fcde2e 100644 --- a/src/gateway/model-pricing-cache.test.ts +++ b/src/gateway/model-pricing-cache.test.ts @@ -1107,7 +1107,7 @@ describe("model-pricing-cache", () => { "abort", () => { abortedUrls.push(url); - reject(signal.reason); + reject(toLintErrorObject(signal.reason, "Non-Error rejection")); }, { once: true }, ); @@ -1277,3 +1277,17 @@ function createManifestRecord(overrides: Partial): PluginM ...overrides, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/gateway/probe.ts b/src/gateway/probe.ts index ec805166592..b479c426413 100644 --- a/src/gateway/probe.ts +++ b/src/gateway/probe.ts @@ -498,7 +498,7 @@ export async function probeGateway(opts: { configSnapshot: null, }); }) - .catch((err) => { + .catch((err: unknown) => { if (settled) { return; } diff --git a/src/gateway/server-channels.ts b/src/gateway/server-channels.ts index 57087c78b6b..bf57b1466a2 100644 --- a/src/gateway/server-channels.ts +++ b/src/gateway/server-channels.ts @@ -577,7 +577,7 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage setRuntime(channelId, id, { accountId: id, lastError: message }); log.error?.(`[${id}] ${message}`); }) - .catch((err) => { + .catch((err: unknown) => { const message = formatErrorMessage(err); setRuntime(channelId, id, { accountId: id, lastError: message }); log.error?.(`[${id}] channel exited: ${message}`); diff --git a/src/gateway/server-control-ui-root.ts b/src/gateway/server-control-ui-root.ts index ff7760adfdc..93fc25d68b4 100644 --- a/src/gateway/server-control-ui-root.ts +++ b/src/gateway/server-control-ui-root.ts @@ -18,7 +18,7 @@ function startControlUiAssetsBuild(params: { params.log.warn(`gateway: ${result.message}`); } }) - .catch((error) => { + .catch((error: unknown) => { params.log.warn( `gateway: Control UI assets build failed: ${error instanceof Error ? error.message : String(error)}`, ); diff --git a/src/gateway/server-cron.ts b/src/gateway/server-cron.ts index 78f5af32b8d..be9f4edc126 100644 --- a/src/gateway/server-cron.ts +++ b/src/gateway/server-cron.ts @@ -293,7 +293,7 @@ export function buildGatewayCronService(params: { config: getRuntimeConfig(), getCron: () => cron as PluginHookGatewayCronService, }; - void hookRunner.runCronChanged(evt, hookCtx).catch((err) => { + void hookRunner.runCronChanged(evt, hookCtx).catch((err: unknown) => { cronLogger.warn( { err: formatErrorMessage(err), jobId: evt.jobId }, "cron_changed hook failed", @@ -501,7 +501,7 @@ export function buildGatewayCronService(params: { usage: evt.usage, }, opts: { keepLines: runLogPrune.keepLines }, - }).catch((err) => { + }).catch((err: unknown) => { cronLogger.warn( { err: String(err), storePath, jobId: evt.jobId }, "cron: run log append failed", diff --git a/src/gateway/server-discovery-runtime.ts b/src/gateway/server-discovery-runtime.ts index 729ce70c2f4..1ce59395f05 100644 --- a/src/gateway/server-discovery-runtime.ts +++ b/src/gateway/server-discovery-runtime.ts @@ -101,7 +101,7 @@ export async function startGatewayDiscovery(params: { } return started; }, - (err) => { + (err: unknown) => { params.logDiscovery.warn( `gateway discovery service failed${timedOut ? " after startup timeout" : ""} (${entry.service.id}, plugin=${entry.pluginId}): ${String(err)}`, ); diff --git a/src/gateway/server-http.ts b/src/gateway/server-http.ts index 8cff956d1fa..4d8a4e1ed54 100644 --- a/src/gateway/server-http.ts +++ b/src/gateway/server-http.ts @@ -191,7 +191,7 @@ function getCachedPluginGatewayAuthBypassPaths( if (cached) { return cached; } - const resolved = resolvePluginGatewayAuthBypassPaths(configSnapshot).catch((error) => { + const resolved = resolvePluginGatewayAuthBypassPaths(configSnapshot).catch((error: unknown) => { pluginGatewayAuthBypassPathsCache.delete(configSnapshot); throw error; }); @@ -982,7 +982,7 @@ export function attachGatewayUpgradeHandler(opts: { releaseUpgradeBudget(); throw new Error("gateway websocket upgrade failed"); } - }).catch((err) => { + }).catch((err: unknown) => { const remoteAddress = (socket as { remoteAddress?: string }).remoteAddress ?? "unknown"; const errorMessage = err instanceof Error ? err.message : String(err); log?.warn(`ws upgrade error from ${remoteAddress}: ${errorMessage}`); diff --git a/src/gateway/server-maintenance.ts b/src/gateway/server-maintenance.ts index 90823ef9dc2..0daa24cb0de 100644 --- a/src/gateway/server-maintenance.ts +++ b/src/gateway/server-maintenance.ts @@ -83,13 +83,13 @@ export function startGatewayMaintenanceTimers(params: { const healthInterval = setInterval(() => { void params .refreshGatewayHealthSnapshot({ probe: false }) - .catch((err) => params.logHealth.error(`refresh failed: ${formatError(err)}`)); + .catch((err: unknown) => params.logHealth.error(`refresh failed: ${formatError(err)}`)); }, HEALTH_REFRESH_INTERVAL_MS); // Prime cache so first client gets a snapshot without waiting. void params .refreshGatewayHealthSnapshot({ probe: false }) - .catch((err) => params.logHealth.error(`initial refresh failed: ${formatError(err)}`)); + .catch((err: unknown) => params.logHealth.error(`initial refresh failed: ${formatError(err)}`)); // dedupe cache cleanup const dedupeCleanup = setInterval(() => { @@ -272,7 +272,7 @@ export function startGatewayMaintenanceTimers(params: { recursive: true, pruneEmptyDirs: true, }) - .catch((err) => { + .catch((err: unknown) => { params.logHealth.error(`media cleanup failed: ${formatError(err)}`); }) .finally(() => { diff --git a/src/gateway/server-methods/agent.test.ts b/src/gateway/server-methods/agent.test.ts index 10536b0b2d6..0dc0e184d74 100644 --- a/src/gateway/server-methods/agent.test.ts +++ b/src/gateway/server-methods/agent.test.ts @@ -233,7 +233,10 @@ async function waitForAssertion(assertion: () => void, timeoutMs = 2_000, stepMs await waitForRealTimer(stepMs); } } - throw lastError ?? new Error("assertion did not pass in time"); + throw toLintErrorObject( + lastError ?? new Error("assertion did not pass in time"), + "Non-Error thrown", + ); } function requireValue(value: T | null | undefined, message: string): T { @@ -6513,3 +6516,17 @@ describe("gateway agent handler chat.abort integration", () => { ); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/gateway/server-methods/agent.ts b/src/gateway/server-methods/agent.ts index 87ef4f0b05e..6bf5aa21147 100644 --- a/src/gateway/server-methods/agent.ts +++ b/src/gateway/server-methods/agent.ts @@ -944,7 +944,7 @@ function dispatchAgentRunFromGateway(params: { // Swift clients will typically treat the first res as the result and ignore this. params.respond(true, payload, undefined, { runId: params.runId }); }) - .catch((err) => { + .catch((err: unknown) => { const aborted = isGatewayAgentAbortRejection(err, params.abortController.signal); const renderedErr = formatForLog(err); if (taskTracked) { diff --git a/src/gateway/server-methods/channels.ts b/src/gateway/server-methods/channels.ts index cffe2447864..66cfae7fc51 100644 --- a/src/gateway/server-methods/channels.ts +++ b/src/gateway/server-methods/channels.ts @@ -127,7 +127,7 @@ async function raceWithTimeout(params: { .then(params.run) .then( (value) => ({ kind: "value" as const, value }), - (error) => ({ kind: "error" as const, error }), + (error: unknown) => ({ kind: "error" as const, error }), ), timeout, ]); diff --git a/src/gateway/server-methods/chat.ts b/src/gateway/server-methods/chat.ts index ab663d97ded..27110dd87d4 100644 --- a/src/gateway/server-methods/chat.ts +++ b/src/gateway/server-methods/chat.ts @@ -739,7 +739,7 @@ function scheduleChatHistoryManagedImageCleanup(params: { ...(params.sessionKey === "global" && params.agentId ? { agentId: params.agentId } : {}), }) .then(() => undefined) - .catch((error) => { + .catch((error: unknown) => { params.context.logGateway.debug( `chat.history managed image cleanup skipped sessionKey=${JSON.stringify(params.sessionKey)} error=${formatForLog(error)}`, ); @@ -4150,12 +4150,12 @@ export const chatHandlers: GatewayRequestHandlers = { }, ); }) - .catch(async (err) => { + .catch(async (err: unknown) => { const emitAfterError = userTurnRecorder.hasPersisted() || userTurnRecorder.isBlocked() ? Promise.resolve() : persistGatewayUserTurnTranscript(); - await emitAfterError.catch((transcriptErr) => { + await emitAfterError.catch((transcriptErr: unknown) => { context.logGateway.warn( `webchat user transcript update failed after error: ${formatForLog(transcriptErr)}`, ); diff --git a/src/gateway/server-methods/config.ts b/src/gateway/server-methods/config.ts index db670b5e2fd..3879d515dd3 100644 --- a/src/gateway/server-methods/config.ts +++ b/src/gateway/server-methods/config.ts @@ -178,7 +178,7 @@ function execConfigOpenCommand(command: ConfigOpenCommand): Promise { return new Promise((resolve, reject) => { execFile(command.command, command.args, (error) => { if (error) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); return; } resolve(); @@ -738,3 +738,17 @@ export const configHandlers: GatewayRequestHandlers = { } }, }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/gateway/server-methods/exec-approval.ts b/src/gateway/server-methods/exec-approval.ts index dd017c8ab10..c88fc981914 100644 --- a/src/gateway/server-methods/exec-approval.ts +++ b/src/gateway/server-methods/exec-approval.ts @@ -355,7 +355,7 @@ export function createExecApprovalHandlers( const deliveryTasks: Array> = []; if (opts?.forwarder) { deliveryTasks.push( - opts.forwarder.handleRequested(requestEvent).catch((err) => { + opts.forwarder.handleRequested(requestEvent).catch((err: unknown) => { context.logGateway?.error?.( `exec approvals: forward request failed: ${String(err)}`, ); @@ -379,7 +379,7 @@ export function createExecApprovalHandlers( } as GatewayClient, }), }) - .catch((err) => { + .catch((err: unknown) => { context.logGateway?.error?.( `exec approvals: iOS push request failed: ${String(err)}`, ); diff --git a/src/gateway/server-methods/health.ts b/src/gateway/server-methods/health.ts index 407f031c6f4..0baa7a2046f 100644 --- a/src/gateway/server-methods/health.ts +++ b/src/gateway/server-methods/health.ts @@ -153,7 +153,7 @@ export const healthHandlers: GatewayRequestHandlers = { ); // Serve the fresh-enough cache immediately but still refresh in the // background so the next caller sees updated expensive probe data. - void refreshHealthSnapshot({ probe: false, includeSensitive }).catch((err) => + void refreshHealthSnapshot({ probe: false, includeSensitive }).catch((err: unknown) => logHealth.error(`background health refresh failed: ${formatError(err)}`), ); return; diff --git a/src/gateway/server-methods/models-auth-status.ts b/src/gateway/server-methods/models-auth-status.ts index eda713ffa31..41270c4c2ee 100644 --- a/src/gateway/server-methods/models-auth-status.ts +++ b/src/gateway/server-methods/models-auth-status.ts @@ -409,9 +409,11 @@ export const modelsAuthStatusHandlers: GatewayRequestHandlers = { await refreshActiveSecretsRuntimeSnapshot(); invalidateModelAuthStatusCache(); clearCurrentProviderAuthState(); - void warmCurrentProviderAuthStateOffMainThread(context.getRuntimeConfig()).catch((err) => { - log.warn(`provider auth state rewarm after logout failed: ${formatForLog(err)}`); - }); + void warmCurrentProviderAuthStateOffMainThread(context.getRuntimeConfig()).catch( + (err: unknown) => { + log.warn(`provider auth state rewarm after logout failed: ${formatForLog(err)}`); + }, + ); const { runIds: abortedRunIds } = abortChatRunsForProvider( createAuthLogoutAbortOps(context), { diff --git a/src/gateway/server-methods/nodes.ts b/src/gateway/server-methods/nodes.ts index aac5a7b7b19..72731725aca 100644 --- a/src/gateway/server-methods/nodes.ts +++ b/src/gateway/server-methods/nodes.ts @@ -339,7 +339,7 @@ function refreshConnectedNodeSurfaceCaches(params: { deviceFamily: nodeSession.deviceFamily, commands: nodeSession.commands, cfg, - }).catch((err) => + }).catch((err: unknown) => params.context.logGateway.warn( `remote bin probe failed for ${nodeSession.nodeId}: ${formatErrorMessage(err)}`, ), diff --git a/src/gateway/server-methods/plugin-approval.ts b/src/gateway/server-methods/plugin-approval.ts index 65b89139eb4..0a9fd874395 100644 --- a/src/gateway/server-methods/plugin-approval.ts +++ b/src/gateway/server-methods/plugin-approval.ts @@ -125,10 +125,14 @@ export function createPluginApprovalHandlers( if (!opts?.forwarder?.handlePluginApprovalRequested) { return false; } - return opts.forwarder.handlePluginApprovalRequested(requestEvent).catch((err) => { - context.logGateway?.error?.(`plugin approvals: forward request failed: ${String(err)}`); - return false; - }); + return opts.forwarder + .handlePluginApprovalRequested(requestEvent) + .catch((err: unknown) => { + context.logGateway?.error?.( + `plugin approvals: forward request failed: ${String(err)}`, + ); + return false; + }); }, }); }, diff --git a/src/gateway/server-methods/tools-effective.ts b/src/gateway/server-methods/tools-effective.ts index 06755e98168..a68e33c00a2 100644 --- a/src/gateway/server-methods/tools-effective.ts +++ b/src/gateway/server-methods/tools-effective.ts @@ -190,7 +190,7 @@ function scheduleBaseToolsEffectiveRefresh( } resolve(value); } catch (err) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } finally { toolsEffectiveInflight.delete(key); } @@ -204,7 +204,7 @@ function refreshBaseToolsEffectiveInBackground( key: string, context: TrustedToolsEffectiveContext, ): void { - void scheduleBaseToolsEffectiveRefresh(key, context).catch((err) => { + void scheduleBaseToolsEffectiveRefresh(key, context).catch((err: unknown) => { logWarn(`tools-effective: background refresh failed: ${String(err)}`); }); } @@ -600,3 +600,17 @@ export const testing = { }, } as const; export { testing as __testing }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/gateway/server-methods/usage.ts b/src/gateway/server-methods/usage.ts index 0c90bc6fe43..35355a8ace7 100644 --- a/src/gateway/server-methods/usage.ts +++ b/src/gateway/server-methods/usage.ts @@ -800,7 +800,7 @@ async function loadCostUsageSummaryCached(params: { }); return summary; }) - .catch((err) => { + .catch((err: unknown) => { if (entry.summary) { // Serve the stale summary if background refresh fails; callers asked for usage, not repair. return entry.summary; diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 5d9a81a55ba..7f70b9ed9d8 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -293,7 +293,7 @@ function queueSessionStoreTouch(params: { entry: params.entry, sessionId: params.sessionId, now: params.now, - }).catch((err) => { + }).catch((err: unknown) => { params.ctx.logGateway.warn("voice session-store update failed: " + formatForLog(err)); }); } @@ -431,7 +431,7 @@ export const handleNodeEvent = async ( }, defaultRuntime, ctx.deps, - ).catch((err) => { + ).catch((err: unknown) => { ctx.logGateway.warn(`agent failed node=${nodeId}: ${formatForLog(err)}`); }); return undefined; @@ -574,7 +574,7 @@ export const handleNodeEvent = async ( channel: deliveryChannel, to: deliveryTo, text: receiptText, - }).catch((err) => { + }).catch((err: unknown) => { ctx.logGateway.warn(`agent receipt failed node=${nodeId}: ${formatForLog(err)}`); }); } else if (wantsReceipt) { @@ -602,7 +602,7 @@ export const handleNodeEvent = async ( }, defaultRuntime, ctx.deps, - ).catch((err) => { + ).catch((err: unknown) => { ctx.logGateway.warn(`agent failed node=${nodeId}: ${formatForLog(err)}`); }); return undefined; diff --git a/src/gateway/server-reload-handlers.ts b/src/gateway/server-reload-handlers.ts index ed752a82d80..c2d674a3c3d 100644 --- a/src/gateway/server-reload-handlers.ts +++ b/src/gateway/server-reload-handlers.ts @@ -440,7 +440,7 @@ export function createGatewayReloadHandlers(params: GatewayReloadHandlerParams) import("../hooks/gmail-watcher-lifecycle.js"), ]); if (!restartAbortController.signal.aborted) { - await stopGmailWatcher().catch((err) => { + await stopGmailWatcher().catch((err: unknown) => { params.logHooks.warn(`gmail watcher stop failed during reload: ${String(err)}`); }); } @@ -500,7 +500,7 @@ export function createGatewayReloadHandlers(params: GatewayReloadHandlerParams) applyGatewayLaneConcurrency(nextConfig); - void warmCurrentProviderAuthStateOffMainThread(nextConfig).catch((err) => { + void warmCurrentProviderAuthStateOffMainThread(nextConfig).catch((err: unknown) => { params.logReload.warn(`provider auth state rewarm failed: ${String(err)}`); }); diff --git a/src/gateway/server-restart-sentinel.ts b/src/gateway/server-restart-sentinel.ts index 222647b2e98..0d14988a978 100644 --- a/src/gateway/server-restart-sentinel.ts +++ b/src/gateway/server-restart-sentinel.ts @@ -348,7 +348,7 @@ async function deliverQueuedSessionDelivery(params: { }, }); if (dispatchError) { - throw dispatchError; + throw toLintErrorObject(dispatchError, "Non-Error thrown"); } } @@ -658,3 +658,17 @@ export function getLatestUpdateRestartSentinel(): RestartSentinelPayload | null export function recordLatestUpdateRestartSentinel(payload: RestartSentinelPayload): void { latestUpdateRestartSentinel = cloneRestartSentinelPayload(payload); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/gateway/server-runtime-services.ts b/src/gateway/server-runtime-services.ts index d7e4c63a903..556cbc9351e 100644 --- a/src/gateway/server-runtime-services.ts +++ b/src/gateway/server-runtime-services.ts @@ -37,7 +37,9 @@ export function startGatewayCronWithLogging(params: { cron: { start: () => Promise }; logCron: { error: (message: string) => void }; }): void { - void params.cron.start().catch((err) => params.logCron.error(`failed to start: ${String(err)}`)); + void params.cron + .start() + .catch((err: unknown) => params.logCron.error(`failed to start: ${String(err)}`)); } function clearGatewayMaintenanceHandles(maintenance: GatewayMaintenanceHandles | null): void { @@ -152,7 +154,7 @@ function recoverPendingOutboundDeliveries(params: { log: logRecovery, cfg: params.cfg, }); - })().catch((err) => params.log.error(`Delivery recovery failed: ${String(err)}`)); + })().catch((err: unknown) => params.log.error(`Delivery recovery failed: ${String(err)}`)); } function recoverPendingSessionDeliveries(params: { @@ -172,7 +174,9 @@ function recoverPendingSessionDeliveries(params: { log: logRecovery, maxEnqueuedAt: params.maxEnqueuedAt, }); - })().catch((err) => params.log.error(`Session delivery recovery failed: ${String(err)}`)); + })().catch((err: unknown) => + params.log.error(`Session delivery recovery failed: ${String(err)}`), + ); }, 1_250); timer.unref?.(); } @@ -202,7 +206,9 @@ function startGatewayModelPricingRefreshOnDemand(params: { stopRefresh(); stopRefresh = undefined; } - })().catch((err) => params.log.error(`Model pricing refresh failed to start: ${String(err)}`)); + })().catch((err: unknown) => + params.log.error(`Model pricing refresh failed to start: ${String(err)}`), + ); return () => { stopped = true; stopRefresh?.(); diff --git a/src/gateway/server-startup-memory.ts b/src/gateway/server-startup-memory.ts index 9b2c4fd5c4e..e56f6fcfef6 100644 --- a/src/gateway/server-startup-memory.ts +++ b/src/gateway/server-startup-memory.ts @@ -90,7 +90,7 @@ export async function startGatewayMemoryBackend(params: { params.log.warn(`qmd memory startup boot sync failed for agent "${agentId}": ${String(err)}`); continue; } finally { - await manager.close?.().catch((err) => { + await manager.close?.().catch((err: unknown) => { params.log.warn( `qmd memory startup manager close failed for agent "${agentId}": ${String(err)}`, ); diff --git a/src/gateway/server-startup-post-attach.ts b/src/gateway/server-startup-post-attach.ts index 15585035ff2..a578f5cbbb0 100644 --- a/src/gateway/server-startup-post-attach.ts +++ b/src/gateway/server-startup-post-attach.ts @@ -184,7 +184,7 @@ function scheduleGatewayMemoryBackend(params: { .then(({ startGatewayMemoryBackend }) => startGatewayMemoryBackend({ cfg: params.cfg, log: params.log }), ) - .catch((err) => { + .catch((err: unknown) => { params.log.warn(`qmd memory startup initialization failed: ${String(err)}`); }); }; @@ -210,7 +210,7 @@ function schedulePostAttachUpdateSentinelRefresh(params: { } catch (err) { params.log.warn(`restart sentinel refresh failed: ${String(err)}`); } - }).catch((err) => { + }).catch((err: unknown) => { params.log.warn(`restart sentinel refresh failed: ${String(err)}`); }); }); @@ -307,14 +307,14 @@ function scheduleProviderAuthStatePrewarm(params: { params.log.info( `provider auth state pre-warmed ${formatProviderAuthWarmMetrics(metrics)}`, ); - })().catch((err) => { + })().catch((err: unknown) => { params.log.warn(`provider auth state pre-warm failed: ${String(err)}`); }); }, Math.max(0, delayMs), ); startupTimer.unref?.(); - })().catch((err) => { + })().catch((err: unknown) => { params.log.warn(`provider auth state pre-warm setup failed: ${String(err)}`); }); return { @@ -368,7 +368,7 @@ function scheduleAgentRuntimePluginPrewarm(params: { `agent runtime plugins pre-warmed in ${(performance.now() - started).toFixed(0)}ms`, ); } - }).catch((err) => { + }).catch((err: unknown) => { params.log.warn(`agent runtime plugin pre-warm failed: ${String(err)}`); }); }, @@ -402,7 +402,7 @@ function schedulePostReadySidecarTask(params: { } void measureStartup(params.startupTrace, params.name, () => params.run(isStopped, abortController.signal), - ).catch((err) => { + ).catch((err: unknown) => { params.log.warn(`${params.name} failed after gateway ready: ${String(err)}`); }); }); @@ -685,7 +685,7 @@ async function prewarmConfiguredPrimaryModelWithTimeout( ): Promise { let settled = false; const warmup = prewarm(params) - .catch((err) => { + .catch((err: unknown) => { params.log.warn(`startup model warmup failed: ${String(err)}`); }) .finally(() => { @@ -724,7 +724,7 @@ function schedulePrimaryModelPrewarm( }, prewarm, ), - ).catch((err) => { + ).catch((err: unknown) => { params.log.warn(`startup model warmup failed: ${String(err)}`); }); } @@ -821,7 +821,7 @@ export async function startGatewaySidecars(params: { } }); if (pluginServices && params.shouldStartPluginServices?.() === false) { - await pluginServices.stop().catch((err) => { + await pluginServices.stop().catch((err: unknown) => { params.log.warn(`plugin services stop after close failed: ${String(err)}`); }); pluginServices = null; @@ -870,7 +870,7 @@ export async function startGatewaySidecars(params: { `acp startup identity reconcile (renderer=${ACP_SESSION_IDENTITY_RENDERER_VERSION}): checked=${result.checked} resolved=${result.resolved} failed=${result.failed}`, ); }); - })().catch((err) => { + })().catch((err: unknown) => { params.log.warn(`acp startup identity reconcile failed: ${String(err)}`); }); } @@ -924,7 +924,7 @@ export async function startGatewaySidecars(params: { .then(({ scheduleRestartSentinelWake }) => scheduleRestartSentinelWake({ deps: params.deps }), ) - .catch((err) => { + .catch((err: unknown) => { params.log.warn(`restart sentinel wake failed to schedule: ${String(err)}`); }); }, 750); @@ -1109,7 +1109,7 @@ function createDeferredGatewayUpdateCheck(params: { } stopUpdateCheck = nextStop; }) - .catch((err) => { + .catch((err: unknown) => { if (stopped) { return; } @@ -1412,12 +1412,12 @@ export async function startGatewayPostAttachRuntime( (params.deps.cron as PluginHookGatewayCronService | undefined), }, ), - ).catch((err) => { + ).catch((err: unknown) => { params.log.warn(`gateway_start hook failed: ${String(err)}`); }); } }) - .catch((err) => { + .catch((err: unknown) => { params.log.warn(`gateway sidecars failed to start: ${String(err)}`); }); diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index 9540d741d9f..4ed5ed1b0c0 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -1327,7 +1327,7 @@ export async function startGatewayServer( const previousPluginServices = runtimeState.pluginServices; runtimeState.pluginServices = null; if (previousPluginServices) { - await previousPluginServices.stop().catch((err) => { + await previousPluginServices.stop().catch((err: unknown) => { log.warn(`plugin services stop failed during reload: ${String(err)}`); }); } @@ -1706,7 +1706,7 @@ export async function startGatewayServer( sharedGatewaySessionGenerationState, clients, }); - await promoteConfigSnapshotToLastKnownGood(startupLastGoodSnapshot).catch((err) => { + await promoteConfigSnapshotToLastKnownGood(startupLastGoodSnapshot).catch((err: unknown) => { log.warn(`gateway: failed to promote config last-known-good backup: ${String(err)}`); }); if (!minimalTestGateway) { diff --git a/src/gateway/server.plugin-node-capability-auth.test.ts b/src/gateway/server.plugin-node-capability-auth.test.ts index ff6bfc3d6db..1bd9745b2f5 100644 --- a/src/gateway/server.plugin-node-capability-auth.test.ts +++ b/src/gateway/server.plugin-node-capability-auth.test.ts @@ -204,7 +204,7 @@ async function sendRawHttpRequest(params: { }); }); socket.on("data", (chunk) => { - response += chunk; + response += chunk.toString(); }); socket.once("end", () => { finish(() => resolve(response)); diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index d92926cec6b..b0bd1e08e12 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -1779,7 +1779,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar for (const nodeId of nodeIdsForPairing) { void updatePairedNodeMetadata(nodeId, { lastConnectedAtMs: nodeSession.connectedAtMs, - }).catch((err) => + }).catch((err: unknown) => logGateway.warn(`failed to record last connect for ${nodeId}: ${formatForLog(err)}`), ); } @@ -1797,7 +1797,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar deviceFamily: nodeSession.deviceFamily, commands: nodeSession.commands, cfg: getRuntimeConfig(), - }).catch((err) => + }).catch((err: unknown) => logGateway.warn( `remote bin probe failed for ${nodeSession.nodeId}: ${formatForLog(err)}`, ), @@ -1808,7 +1808,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar triggers: cfg.triggers, }); }) - .catch((err) => + .catch((err: unknown) => logGateway.warn( `voicewake snapshot failed for ${nodeSession.nodeId}: ${formatForLog(err)}`, ), @@ -1819,7 +1819,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar config: routing, }); }) - .catch((err) => + .catch((err: unknown) => logGateway.warn( `voicewake routing snapshot failed for ${nodeSession.nodeId}: ${formatForLog(err)}`, ), @@ -1921,7 +1921,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar // Post-connect refresh only needs a cached/config snapshot for UI state; // live channel probes here pulled slow Discord/Telegram HTTP checks into // reply-adjacent websocket handshakes. - void refreshHealthSnapshot({ probe: false }).catch((err) => + void refreshHealthSnapshot({ probe: false }).catch((err: unknown) => logHealth.error(`post-connect health refresh failed: ${formatError(err)}`), ); return; @@ -2026,7 +2026,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar methodRegistry: getMethodRegistry?.(), context: buildRequestContext(), }); - })().catch((err) => { + })().catch((err: unknown) => { logGateway.error(`request handler failed: ${formatForLog(err)}`); respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, formatForLog(err))); }); diff --git a/src/gateway/session-reset-service.ts b/src/gateway/session-reset-service.ts index 74515803f82..1b0f36896f9 100644 --- a/src/gateway/session-reset-service.ts +++ b/src/gateway/session-reset-service.ts @@ -197,7 +197,7 @@ export function emitGatewaySessionEndPluginHook(params: { nextSessionId: params.nextSessionId, nextSessionKey: params.nextSessionKey, }); - void hookRunner.runSessionEnd(payload.event, payload.context).catch((err) => { + void hookRunner.runSessionEnd(payload.event, payload.context).catch((err: unknown) => { logVerbose(`session_end hook failed: ${String(err)}`); }); } @@ -240,7 +240,7 @@ export function emitGatewaySessionStartPluginHook(params: { cfg: params.cfg, resumedFrom: params.resumedFrom, }); - void hookRunner.runSessionStart(payload.event, payload.context).catch((err) => { + void hookRunner.runSessionStart(payload.event, payload.context).catch((err: unknown) => { logVerbose(`session_start hook failed: ${String(err)}`); }); } @@ -737,7 +737,7 @@ export async function emitGatewayBeforeResetPluginHook(params: { workspaceDir, }, ) - .catch((err) => { + .catch((err: unknown) => { logVerbose(`before_reset hook failed: ${String(err)}`); }); } diff --git a/src/gateway/sessions-history-http.ts b/src/gateway/sessions-history-http.ts index f3618761048..e5f2176b441 100644 --- a/src/gateway/sessions-history-http.ts +++ b/src/gateway/sessions-history-http.ts @@ -256,7 +256,7 @@ export async function handleSessionHistoryHttpRequest( } await work(); }) - .catch((error) => { + .catch((error: unknown) => { // Surface the underlying error so operators can distinguish transient // infrastructure failures (for example a `getRuntimeConfig()` read error // inside the reauth path) from deliberate revocation, then fail closed. diff --git a/src/gateway/tools-invoke-http.cron-regression.test.ts b/src/gateway/tools-invoke-http.cron-regression.test.ts index 95a0e3b3888..ee1b33c8b85 100644 --- a/src/gateway/tools-invoke-http.cron-regression.test.ts +++ b/src/gateway/tools-invoke-http.cron-regression.test.ts @@ -89,7 +89,7 @@ beforeAll(async () => { } res.statusCode = 404; res.end("not found"); - })().catch((err) => { + })().catch((err: unknown) => { res.statusCode = 500; res.end(String(err)); }); diff --git a/src/gateway/tools-invoke-http.test.ts b/src/gateway/tools-invoke-http.test.ts index 88e4488ed55..12856d194d4 100644 --- a/src/gateway/tools-invoke-http.test.ts +++ b/src/gateway/tools-invoke-http.test.ts @@ -236,7 +236,7 @@ beforeAll(async () => { } res.statusCode = 404; res.end("not found"); - })().catch((err) => { + })().catch((err: unknown) => { res.statusCode = 500; res.end(String(err)); }); diff --git a/src/hooks/fire-and-forget.ts b/src/hooks/fire-and-forget.ts index aba7ae1481c..498172dd3ec 100644 --- a/src/hooks/fire-and-forget.ts +++ b/src/hooks/fire-and-forget.ts @@ -77,7 +77,7 @@ export function fireAndForgetHook( label: string, logger: (message: string) => void = logVerbose, ): void { - void task.catch((err) => { + void task.catch((err: unknown) => { logger(`${label}: ${formatHookErrorForLog(err)}`); }); } @@ -99,7 +99,7 @@ function runFireAndForgetHookJob( void Promise.resolve() .then(job.task) - .catch((err) => { + .catch((err: unknown) => { if (!didLogTimeout) { job.logger(`${job.label}: ${formatHookErrorForLog(err)}`); } diff --git a/src/index.ts b/src/index.ts index 572697e6d0d..67ad624b5d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -115,7 +115,7 @@ if (isMain) { process.exit(1); }); - void runLegacyCliEntry(process.argv).catch((err) => { + void runLegacyCliEntry(process.argv).catch((err: unknown) => { for (const line of formatCliFailureLines({ title: "The CLI command failed.", error: err, diff --git a/src/infra/approval-handler-bootstrap.ts b/src/infra/approval-handler-bootstrap.ts index 34f7663e697..d5ae6ff7986 100644 --- a/src/infra/approval-handler-bootstrap.ts +++ b/src/infra/approval-handler-bootstrap.ts @@ -114,7 +114,7 @@ export async function startChannelApprovalHandlerBootstrap(params: { }; const spawn = (label: string, promise: Promise) => { - void promise.catch((error) => { + void promise.catch((error: unknown) => { logger.error(`${label}: ${String(error)}`); }); }; diff --git a/src/infra/clawhub.ts b/src/infra/clawhub.ts index 9d88577fe62..186a70c0d89 100644 --- a/src/infra/clawhub.ts +++ b/src/infra/clawhub.ts @@ -599,7 +599,6 @@ function satisfiesComparator(version: string, token: string): boolean { return cmp > 0; case "<": return cmp < 0; - case "=": default: return normalizedTarget.isPartial && !operator ? cmp >= 0 : cmp === 0; } diff --git a/src/infra/exec-approval-channel-runtime.test.ts b/src/infra/exec-approval-channel-runtime.test.ts index 51577378a7a..c5063617485 100644 --- a/src/infra/exec-approval-channel-runtime.test.ts +++ b/src/infra/exec-approval-channel-runtime.test.ts @@ -450,7 +450,7 @@ describe("createExecApprovalChannelRuntime", () => { }); let caught: unknown; - await runtime.start().catch((error) => { + await runtime.start().catch((error: unknown) => { caught = error; }); diff --git a/src/infra/exec-approval-forwarder.ts b/src/infra/exec-approval-forwarder.ts index ca44743a5b5..8035a42b431 100644 --- a/src/infra/exec-approval-forwarder.ts +++ b/src/infra/exec-approval-forwarder.ts @@ -580,7 +580,7 @@ function createApprovalHandlers< buildPayload: () => ({ text: params.strategy.buildExpiredText(request) }), deliver: params.deliver, }); - })().catch((err) => { + })().catch((err: unknown) => { log.error( `${params.strategy.kind} approvals: failed to deliver expiry notification for ${requestId}: ${String(err)}`, ); @@ -627,7 +627,7 @@ function createApprovalHandlers< }, deliver: params.deliver, shouldSend: () => pending.get(requestId) === pendingEntry, - }).catch((err) => { + }).catch((err: unknown) => { log.error( `${params.strategy.kind} approvals: failed to deliver request ${requestId}: ${String(err)}`, ); diff --git a/src/infra/net/fetch-guard.ssrf.test.ts b/src/infra/net/fetch-guard.ssrf.test.ts index b8dc91f0668..68ee01fbe1f 100644 --- a/src/infra/net/fetch-guard.ssrf.test.ts +++ b/src/infra/net/fetch-guard.ssrf.test.ts @@ -1739,7 +1739,9 @@ describe("fetchWithSsrFGuard hardening", () => { (_input: RequestInfo | URL, init?: RequestInit) => new Promise((_resolve, reject) => { init?.signal?.addEventListener("abort", () => { - reject(init.signal?.reason ?? new Error("aborted")); + reject( + toLintErrorObject(init.signal?.reason ?? new Error("aborted"), "Non-Error rejection"), + ); }); }), ); @@ -2108,3 +2110,17 @@ describe("fetchWithSsrFGuard hardening", () => { await result.release(); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/infra/npm-managed-root.ts b/src/infra/npm-managed-root.ts index d53b5593fe9..567d7bb3325 100644 --- a/src/infra/npm-managed-root.ts +++ b/src/infra/npm-managed-root.ts @@ -5,6 +5,7 @@ import path from "node:path"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; import { normalizeOptionalString as readOptionalString } from "@openclaw/normalization-core/string-coerce"; import { runCommandWithTimeout } from "../process/exec.js"; +import { hasErrnoCode } from "./errors.js"; import type { NpmSpecResolution } from "./install-source-utils.js"; import { readJson, readJsonIfExists, writeJson } from "./json-files.js"; import type { ParsedRegistryNpmSpec } from "./npm-registry-spec.js"; @@ -918,8 +919,8 @@ async function pathExists(filePath: string): Promise { return await fs .lstat(filePath) .then(() => true) - .catch((err: NodeJS.ErrnoException) => { - if (err.code === "ENOENT") { + .catch((err: unknown) => { + if (hasErrnoCode(err, "ENOENT")) { return false; } throw err; diff --git a/src/infra/outbound/session-binding-service.test.ts b/src/infra/outbound/session-binding-service.test.ts index 07998ae6709..8fd203082df 100644 --- a/src/infra/outbound/session-binding-service.test.ts +++ b/src/infra/outbound/session-binding-service.test.ts @@ -215,13 +215,14 @@ describe("session binding service", () => { }, placement: "child", }) - .catch((error) => error); + .catch((error: unknown) => error); expect(isSessionBindingError(rejected)).toBe(true); - expectRecordFields(requireRecord(rejected, "session binding error"), { + const rejectedRecord = requireRecord(rejected, "session binding error"); + expectRecordFields(rejectedRecord, { code: "BINDING_CAPABILITY_UNSUPPORTED", }); - expectRecordFields(requireRecord(rejected.details, "session binding details"), { + expectRecordFields(requireRecord(rejectedRecord.details, "session binding details"), { placement: "child", }); }); diff --git a/src/infra/push-apns-http2.ts b/src/infra/push-apns-http2.ts index 6b9197d9199..ece17ae3586 100644 --- a/src/infra/push-apns-http2.ts +++ b/src/infra/push-apns-http2.ts @@ -149,7 +149,7 @@ export async function probeApnsHttp2ReachabilityViaProxy( settled = true; cleanup(); session.destroy(err instanceof Error ? err : new Error(String(err))); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }; const request = session.request({ @@ -198,3 +198,17 @@ export async function probeApnsHttp2ReachabilityViaProxy( } } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/infra/push-apns.ts b/src/infra/push-apns.ts index 98565e16a3d..e54f827fc4a 100644 --- a/src/infra/push-apns.ts +++ b/src/infra/push-apns.ts @@ -725,7 +725,7 @@ async function sendApnsRequest(params: { } settled = true; client.destroy(); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }; const finish = (result: { status: number; apnsId?: string; body: string }) => { if (settled) { @@ -1213,3 +1213,17 @@ export async function sendApnsExecApprovalResolvedWake( } export { type ApnsRelayConfig, resolveApnsRelayConfigFromEnv }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/infra/retry.test.ts b/src/infra/retry.test.ts index 0b8092a0384..9f54065331f 100644 --- a/src/infra/retry.test.ts +++ b/src/infra/retry.test.ts @@ -70,7 +70,7 @@ async function runRetryNumberCase( const promise = retryAsync(fn as () => Promise, attempts, initialDelayMs); const settled = promise.then( (value) => ({ ok: true as const, value }), - (error) => ({ ok: false as const, error }), + (error: unknown) => ({ ok: false as const, error }), ); await vi.runAllTimersAsync(); const result = await settled; diff --git a/src/infra/retry.ts b/src/infra/retry.ts index 7643477c391..62bbfdd34ba 100644 --- a/src/infra/retry.ts +++ b/src/infra/retry.ts @@ -117,7 +117,7 @@ export async function retryAsync( await sleep(delay); } } - throw lastErr ?? new Error("Retry failed"); + throw toLintErrorObject(lastErr ?? new Error("Retry failed"), "Non-Error thrown"); } const options = attemptsOrOptions; @@ -191,5 +191,19 @@ export async function retryAsync( } } - throw lastErr ?? new Error("Retry failed"); + throw toLintErrorObject(lastErr ?? new Error("Retry failed"), "Non-Error thrown"); +} + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; } diff --git a/src/infra/tailscale.ts b/src/infra/tailscale.ts index 1f693c5edb9..29b524b451f 100644 --- a/src/infra/tailscale.ts +++ b/src/infra/tailscale.ts @@ -151,7 +151,10 @@ export async function getTailnetHostname(exec: typeof runExec = runExec, detecte } } - throw lastError ?? new Error("Could not determine Tailscale DNS or IP"); + throw toLintErrorObject( + lastError ?? new Error("Could not determine Tailscale DNS or IP"), + "Non-Error thrown", + ); } /** @@ -615,3 +618,17 @@ export async function readTailscaleWhoisIdentity( return null; } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/llm/providers/openai-chatgpt-responses.ts b/src/llm/providers/openai-chatgpt-responses.ts index 76c05b5c704..77cc3866074 100644 --- a/src/llm/providers/openai-chatgpt-responses.ts +++ b/src/llm/providers/openai-chatgpt-responses.ts @@ -1323,7 +1323,7 @@ async function* parseWebSocket( } if (failed) { - throw failed; + throw toLintErrorObject(failed, "Non-Error thrown"); } if (!sawCompletion) { throw new Error("WebSocket stream closed before response.completed"); @@ -1637,3 +1637,17 @@ function buildWebSocketHeaders( headers.set("session_id", requestId); return headers; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/llm/providers/register-builtins.ts b/src/llm/providers/register-builtins.ts index dfcced3b5a3..2ab119ec6b2 100644 --- a/src/llm/providers/register-builtins.ts +++ b/src/llm/providers/register-builtins.ts @@ -165,7 +165,7 @@ function createLazyStream< const inner = module.stream(model, context, options); forwardStream(outer, inner); }) - .catch((error) => { + .catch((error: unknown) => { const message = createLazyLoadErrorMessage(model, error); outer.push({ type: "error", reason: "error", error: message }); outer.end(message); @@ -190,7 +190,7 @@ function createLazySimpleStream< const inner = module.streamSimple(model, context, options); forwardStream(outer, inner); }) - .catch((error) => { + .catch((error: unknown) => { const message = createLazyLoadErrorMessage(model, error); outer.push({ type: "error", reason: "error", error: message }); outer.end(message); diff --git a/src/llm/utils/oauth/anthropic.ts b/src/llm/utils/oauth/anthropic.ts index 87c1326c140..c68f386a387 100644 --- a/src/llm/utils/oauth/anthropic.ts +++ b/src/llm/utils/oauth/anthropic.ts @@ -326,7 +326,7 @@ export async function loginAnthropic(options: { manualInput = input; server.cancelWait(); }) - .catch((err) => { + .catch((err: unknown) => { manualError = err instanceof Error ? err : new Error(String(err)); server.cancelWait(); }); @@ -357,7 +357,7 @@ export async function loginAnthropic(options: { if (!code) { await withOAuthLoginAbort(manualPromise, options.signal, server.cancelWait); if (manualError) { - throw manualError; + throw toLintErrorObject(manualError, "Non-Error thrown"); } if (manualInput) { const parsed = parseOAuthAuthorizationInput(manualInput); @@ -460,3 +460,17 @@ export const anthropicOAuthProvider: OAuthProviderInterface = { return credentials.access; }, }; + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/logging/diagnostic.ts b/src/logging/diagnostic.ts index 1952b5c79b3..f7a6555e25a 100644 --- a/src/logging/diagnostic.ts +++ b/src/logging/diagnostic.ts @@ -162,7 +162,7 @@ async function recoverStuckSession( stuckSessionRecoveryRuntimePromise ??= import("./diagnostic-stuck-session-recovery.runtime.js"); return stuckSessionRecoveryRuntimePromise .then(({ recoverStuckDiagnosticSession }) => recoverStuckDiagnosticSession(params)) - .catch((err) => { + .catch((err: unknown) => { diag.warn(`stuck session recovery unavailable: ${String(err)}`); return { status: "failed", @@ -1234,7 +1234,7 @@ export function startDiagnosticHeartbeat( pruneStaleCommandPolls(state); } }) - .catch((err) => { + .catch((err: unknown) => { diag.debug(`command-poll-backoff prune failed: ${String(err)}`); }); diff --git a/src/mcp/openclaw-tools-serve.ts b/src/mcp/openclaw-tools-serve.ts index a2a434b9f65..a5bcaba793a 100644 --- a/src/mcp/openclaw-tools-serve.ts +++ b/src/mcp/openclaw-tools-serve.ts @@ -30,7 +30,7 @@ async function serveOpenClawToolsMcp(): Promise { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - serveOpenClawToolsMcp().catch((err) => { + serveOpenClawToolsMcp().catch((err: unknown) => { process.stderr.write(`openclaw-tools-serve: ${formatErrorMessage(err)}\n`); process.exit(1); }); diff --git a/src/mcp/plugin-tools-serve.ts b/src/mcp/plugin-tools-serve.ts index d9f537a3f12..817a3041e7f 100644 --- a/src/mcp/plugin-tools-serve.ts +++ b/src/mcp/plugin-tools-serve.ts @@ -80,7 +80,7 @@ export async function servePluginToolsMcp(): Promise { } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { - servePluginToolsMcp().catch((err) => { + servePluginToolsMcp().catch((err: unknown) => { process.stderr.write(`plugin-tools-serve: ${formatErrorMessage(err)}\n`); process.exit(1); }); diff --git a/src/media-generation/runtime-shared.ts b/src/media-generation/runtime-shared.ts index 67cc71a95f8..23af1349e36 100644 --- a/src/media-generation/runtime-shared.ts +++ b/src/media-generation/runtime-shared.ts @@ -563,7 +563,7 @@ export function throwCapabilityGenerationFailure(params: { lastError: unknown; }): never { if (params.attempts.length <= 1 && params.lastError) { - throw params.lastError; + throw toLintErrorObject(params.lastError, "Non-Error thrown"); } const summary = formatCapabilityFailureAttempts(params.attempts); throw new Error( @@ -643,3 +643,17 @@ export function buildNoCapabilityModelConfiguredMessage(params: { : "If you want a specific provider, also configure that provider's auth/API key first.", ].join(" "); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/media/fetch.ts b/src/media/fetch.ts index 2bca6328e02..e504477087c 100644 --- a/src/media/fetch.ts +++ b/src/media/fetch.ts @@ -408,10 +408,10 @@ async function readChunkWithIdleTimeout( resolve(result); } }, - (err) => { + (err: unknown) => { clear(); if (!timedOut) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } }, ); @@ -683,3 +683,17 @@ async function readRemoteMediaBufferOnce(options: FetchMediaOptions): Promise { if (err) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); return; } if (stdinWriteError && !isBrokenPipeError(stdinWriteError)) { @@ -111,3 +111,17 @@ export function parseFfprobeCodecAndSampleRate(stdout: string): { sampleRateHz: parseFfprobeSampleRateHz(sampleRateRaw), }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/media/store.ts b/src/media/store.ts index a9f017632f9..4fabc842169 100644 --- a/src/media/store.ts +++ b/src/media/store.ts @@ -287,9 +287,9 @@ async function downloadToFile( size: total, }); }) - .catch(async (err) => { + .catch(async (err: unknown) => { await fs.rm(dest, { force: true }).catch(() => {}); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }); }); req.on("error", reject); @@ -505,7 +505,6 @@ function toSaveMediaSourceError(err: FsSafeLikeError, maxBytes = MAX_BYTES): Sav return new SaveMediaSourceError("invalid-path", "Media path is outside workspace root", { cause: err, }); - case "invalid-path": default: return new SaveMediaSourceError("invalid-path", "Media path is not safe to read", { cause: err, @@ -730,3 +729,17 @@ export async function deleteMediaBuffer(id: string, subdir = "inbound"): Promise const relativePath = resolveMediaRelativePath(id, subdir, "deleteMediaBuffer"); await openMediaStore().remove(relativePath); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/node-host/with-timeout.ts b/src/node-host/with-timeout.ts index 841c601b067..f9794119893 100644 --- a/src/node-host/with-timeout.ts +++ b/src/node-host/with-timeout.ts @@ -17,9 +17,12 @@ export async function withTimeout( let abortListener: (() => void) | undefined; const abortPromise: Promise = abortCtrl.signal.aborted - ? Promise.reject(abortCtrl.signal.reason ?? timeoutError) + ? Promise.reject( + toLintErrorObject(abortCtrl.signal.reason ?? timeoutError, "Non-Error rejection"), + ) : new Promise((_, reject) => { - abortListener = () => reject(abortCtrl.signal.reason ?? timeoutError); + abortListener = () => + reject(toLintErrorObject(abortCtrl.signal.reason ?? timeoutError, "Non-Error rejection")); abortCtrl.signal.addEventListener("abort", abortListener, { once: true }); }); @@ -32,3 +35,17 @@ export async function withTimeout( } } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/plugin-sdk/provider-oauth-runtime.ts b/src/plugin-sdk/provider-oauth-runtime.ts index a0edaaaeaf3..3f1607025c6 100644 --- a/src/plugin-sdk/provider-oauth-runtime.ts +++ b/src/plugin-sdk/provider-oauth-runtime.ts @@ -311,9 +311,9 @@ export function withOAuthLoginAbort( cleanup(); resolve(value); }, - (error) => { + (error: unknown) => { cleanup(); - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); }, ); }); @@ -329,3 +329,17 @@ export function buildOAuthRequestSignal(options: { } return AbortSignal.any([options.signal, timeoutSignal]); } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/plugin-sdk/qa-runtime.ts b/src/plugin-sdk/qa-runtime.ts index 9de44d531c1..5d6fa008c92 100644 --- a/src/plugin-sdk/qa-runtime.ts +++ b/src/plugin-sdk/qa-runtime.ts @@ -692,8 +692,22 @@ export async function startLiveTransportQaOutputTee(params: { output.end(resolve); }); if (outputError) { - throw outputError; + throw toLintErrorObject(outputError, "Non-Error thrown"); } }, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/plugins/bundled-plugin-metadata.test.ts b/src/plugins/bundled-plugin-metadata.test.ts index ca149181b5f..bc8a2417c34 100644 --- a/src/plugins/bundled-plugin-metadata.test.ts +++ b/src/plugins/bundled-plugin-metadata.test.ts @@ -274,7 +274,7 @@ function collectRepoBundledChannelConfigsForTest(dirName: string) { const pluginDir = path.join(repoRoot, "extensions", dirName); const manifest = loadPluginManifest(pluginDir, false); if (!manifest.ok) { - throw manifest.error; + throw toLintErrorObject(manifest.error, "Non-Error thrown"); } const configs = collectBundledChannelConfigs({ pluginDir, @@ -1151,3 +1151,17 @@ describe("bundled plugin metadata", () => { expect(fs.existsSync(markerPath)).toBe(false); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/plugins/conversation-binding.ts b/src/plugins/conversation-binding.ts index e6474d4ae9a..41557890511 100644 --- a/src/plugins/conversation-binding.ts +++ b/src/plugins/conversation-binding.ts @@ -946,7 +946,7 @@ function dispatchPluginConversationBindingResolved(params: { }): void { // Keep platform interaction acks fast even if the plugin does slow post-bind work. queueMicrotask(() => { - void notifyPluginConversationBindingResolved(params).catch((error) => { + void notifyPluginConversationBindingResolved(params).catch((error: unknown) => { log.warn(`plugin binding resolved dispatch failed: ${String(error)}`); }); }); diff --git a/src/plugins/host-hook-runtime.ts b/src/plugins/host-hook-runtime.ts index 23e022fe930..bf26723edd3 100644 --- a/src/plugins/host-hook-runtime.ts +++ b/src/plugins/host-hook-runtime.ts @@ -322,7 +322,7 @@ export function dispatchPluginAgentEventSubscriptions(params: { const pending = Promise.resolve( registration.subscription.handle(structuredClone(params.event), ctx), ) - .catch((error) => { + .catch((error: unknown) => { logAgentEventSubscriptionFailure({ pluginId, subscriptionId: registration.subscription.id, diff --git a/src/plugins/marketplace.ts b/src/plugins/marketplace.ts index b63204f33f8..af841e7e247 100644 --- a/src/plugins/marketplace.ts +++ b/src/plugins/marketplace.ts @@ -660,10 +660,10 @@ async function readMarketplaceChunkWithTimeout( resolve(result); } }, - (err) => { + (err: unknown) => { clear(); if (!timedOut) { - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); } }, ); @@ -1176,3 +1176,17 @@ export async function installPluginFromMarketplace( await loaded.marketplace.cleanup?.(); } } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/plugins/plugin-peer-link.ts b/src/plugins/plugin-peer-link.ts index f10d85fec5a..d94f6236096 100644 --- a/src/plugins/plugin-peer-link.ts +++ b/src/plugins/plugin-peer-link.ts @@ -1,5 +1,6 @@ import fs from "node:fs/promises"; import path from "node:path"; +import { hasErrnoCode } from "../infra/errors.js"; import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js"; type PluginPeerLinkLogger = { @@ -73,12 +74,14 @@ async function listManagedNpmRootPackageDirs(npmRoot: string): Promise } const entryPath = path.join(nodeModulesDir, entry.name); if (entry.name.startsWith("@")) { - const scopedEntries = await fs.readdir(entryPath, { withFileTypes: true }).catch((error) => { - if ((error as NodeJS.ErrnoException).code === "ENOENT") { - return []; - } - throw error; - }); + const scopedEntries = await fs + .readdir(entryPath, { withFileTypes: true }) + .catch((error: unknown) => { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return []; + } + throw error; + }); for (const scopedEntry of scopedEntries) { if (scopedEntry.isDirectory()) { packageDirs.push(path.join(entryPath, scopedEntry.name)); @@ -240,8 +243,8 @@ async function linkOpenClawPeerDependency(params: { } try { - const existing = await fs.lstat(linkPath).catch((err: NodeJS.ErrnoException) => { - if (err.code === "ENOENT") { + const existing = await fs.lstat(linkPath).catch((err: unknown) => { + if (hasErrnoCode(err, "ENOENT")) { return null; } throw err; diff --git a/src/plugins/runtime.ts b/src/plugins/runtime.ts index 567d7b420ae..776268d35fa 100644 --- a/src/plugins/runtime.ts +++ b/src/plugins/runtime.ts @@ -103,7 +103,7 @@ function cleanupRetiredPluginHostRegistry(previousRegistry: PluginRegistry): voi } void cleanupPreviousPluginHostRegistry({ previousRegistry, - }).catch((error) => { + }).catch((error: unknown) => { log.warn(`plugin host registry cleanup failed: ${String(error)}`); }); } diff --git a/src/process/command-queue.ts b/src/process/command-queue.ts index 9ee86986a39..a1ad13395bf 100644 --- a/src/process/command-queue.ts +++ b/src/process/command-queue.ts @@ -309,7 +309,7 @@ async function runQueueEntryTask(lane: string, entry: QueueEntry): Promise { + void taskPromise.catch((lateErr: unknown) => { diag.warn( `lane task rejected after timeout: lane=${lane} timeoutMs=${taskTimeoutMs} error="${String(lateErr)}"`, ); diff --git a/src/process/spawn-utils.ts b/src/process/spawn-utils.ts index a068970ac3b..0333a3a3715 100644 --- a/src/process/spawn-utils.ts +++ b/src/process/spawn-utils.ts @@ -64,7 +64,7 @@ async function spawnAndWaitForSpawn( } settled = true; cleanup(); - reject(err); + reject(toLintErrorObject(err, "Non-Error rejection")); }; const onSpawn = () => { finishResolve(); @@ -117,3 +117,17 @@ export async function spawnWithFallback( throw lastError; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/process/supervisor/adapters/child.ts b/src/process/supervisor/adapters/child.ts index 294d808aa59..2c849cfd091 100644 --- a/src/process/supervisor/adapters/child.ts +++ b/src/process/supervisor/adapters/child.ts @@ -325,7 +325,7 @@ export async function createChildAdapter(params: { return waitResult; } if (waitError !== undefined) { - throw waitError; + throw toLintErrorObject(waitError, "Non-Error thrown"); } if (!waitPromise) { waitPromise = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>( @@ -343,7 +343,7 @@ export async function createChildAdapter(params: { const error = waitError; resolveWait = null; rejectWait = null; - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); } }, ); @@ -404,3 +404,17 @@ export async function createChildAdapter(params: { dispose, }; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/process/supervisor/supervisor.ts b/src/process/supervisor/supervisor.ts index 10eff612415..c99eeefe739 100644 --- a/src/process/supervisor/supervisor.ts +++ b/src/process/supervisor/supervisor.ts @@ -312,7 +312,7 @@ export function createProcessSupervisor(): ProcessSupervisor { exitSignal: exit.exitSignal, }); return exit; - })().catch((err) => { + })().catch((err: unknown) => { if (!settled) { settled = true; clearTimers(); diff --git a/src/proxy-capture/proxy-server.managed-proxy.test.ts b/src/proxy-capture/proxy-server.managed-proxy.test.ts index a170c6f6746..e56677f39b4 100644 --- a/src/proxy-capture/proxy-server.managed-proxy.test.ts +++ b/src/proxy-capture/proxy-server.managed-proxy.test.ts @@ -40,7 +40,7 @@ async function connectThroughProxy(proxyUrl: string): Promise { let data = ""; socket.setEncoding("utf8"); socket.on("data", (chunk) => { - data += chunk; + data += chunk.toString(); }); await new Promise((resolve, reject) => { socket.once("error", reject); @@ -61,7 +61,7 @@ async function requestThroughProxy(proxyUrl: string, targetUrl: string): Promise let data = ""; socket.setEncoding("utf8"); socket.on("data", (chunk) => { - data += chunk; + data += chunk.toString(); }); await new Promise((resolve, reject) => { socket.once("error", reject); @@ -81,7 +81,7 @@ async function requestRawThroughProxy(proxyUrl: string, request: string): Promis let data = ""; socket.setEncoding("utf8"); socket.on("data", (chunk) => { - data += chunk; + data += chunk.toString(); }); await new Promise((resolve, reject) => { socket.once("error", reject); diff --git a/src/proxy-capture/runtime.ts b/src/proxy-capture/runtime.ts index d8f803f01fb..f98bcd3f4fb 100644 --- a/src/proxy-capture/runtime.ts +++ b/src/proxy-capture/runtime.ts @@ -404,7 +404,7 @@ export function captureHttpExchange( ...responsePayload, }); }) - .catch((error) => { + .catch((error: unknown) => { store.recordEvent({ ...createHttpCaptureEventBase({ settings, diff --git a/src/security/audit-extra.async.ts b/src/security/audit-extra.async.ts index 9950021291e..d0564b2efe7 100644 --- a/src/security/audit-extra.async.ts +++ b/src/security/audit-extra.async.ts @@ -221,7 +221,7 @@ async function listInstalledPluginDirs(params: { if (!st.ok || !st.isDir) { return { extensionsDir, pluginDirs: [] }; } - const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch((err) => { + const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch((err: unknown) => { params.onReadError?.(err); return []; }); @@ -891,7 +891,7 @@ export async function collectPluginsCodeSafetyFindings(params: { dirPath: pluginPath, includeFiles: forcedScanEntries, summaryCache: params.summaryCache, - }).catch((err) => { + }).catch((err: unknown) => { findings.push({ checkId: "plugins.code_safety.scan_failed", severity: "warn", @@ -971,7 +971,7 @@ export async function collectInstalledSkillsCodeSafetyFindings(params: { const summary = await getCodeSafetySummary({ dirPath: skillDir, summaryCache: params.summaryCache, - }).catch((err) => { + }).catch((err: unknown) => { findings.push({ checkId: "skills.code_safety.scan_failed", severity: "warn", diff --git a/src/security/audit-plugins-trust.ts b/src/security/audit-plugins-trust.ts index d1db31d0f5a..ebab4235f46 100644 --- a/src/security/audit-plugins-trust.ts +++ b/src/security/audit-plugins-trust.ts @@ -137,7 +137,7 @@ async function listInstalledPluginDirs(params: { if (!st?.isDirectory()) { return { extensionsDir, pluginDirs: [] }; } - const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch((err) => { + const entries = await fs.readdir(extensionsDir, { withFileTypes: true }).catch((err: unknown) => { params.onReadError?.(err); return []; }); diff --git a/src/security/audit.ts b/src/security/audit.ts index 097f0485575..b2e1d311065 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -1117,7 +1117,7 @@ async function maybeProbeGateway(params: { }); const res = await params .probe({ url, auth: authResolution.auth, timeoutMs: params.timeoutMs }) - .catch((err) => ({ + .catch((err: unknown) => ({ ok: false, url, connectLatencyMs: null, diff --git a/src/security/fix.ts b/src/security/fix.ts index 40ef1a73813..eccb493a611 100644 --- a/src/security/fix.ts +++ b/src/security/fix.ts @@ -453,7 +453,7 @@ export async function fixSecurityFootguns(opts?: { configPath, cfg: snap.config ?? {}, includePaths, - }).catch((err) => { + }).catch((err: unknown) => { errors.push(`collectSecurityPermissionTargets failed: ${String(err)}`); return [] as SecurityPermissionTarget[]; }); diff --git a/src/skills/lifecycle/upload-store.test.ts b/src/skills/lifecycle/upload-store.test.ts index 74fc61784c3..45d50d5dcd2 100644 --- a/src/skills/lifecycle/upload-store.test.ts +++ b/src/skills/lifecycle/upload-store.test.ts @@ -281,7 +281,7 @@ describe("skill upload store", () => { it("limits active uploads", async () => { await expectUploadError( - Promise.reject(activeUploadLimitError), + Promise.reject(toLintErrorObject(activeUploadLimitError, "Non-Error rejection")), "too many active skill uploads", ); }); @@ -481,3 +481,17 @@ describe("skill upload store", () => { await expectMissingPath(path.join(rootDir, committed.uploadId)); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/tui/tui-pty-test-support.ts b/src/tui/tui-pty-test-support.ts index e7d8f975298..ad49ac87bde 100644 --- a/src/tui/tui-pty-test-support.ts +++ b/src/tui/tui-pty-test-support.ts @@ -30,7 +30,7 @@ export function waitFor(params: { try { result = params.read(); } catch (error) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); return; } if (result !== null) { @@ -169,3 +169,17 @@ export function startPty( opts.activeRuns?.push(run); return run; } + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 74b92f92fdb..7cfcf6e88cc 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -1310,7 +1310,7 @@ export async function runTui(opts: RunTuiOptions): Promise { clearTimeout(hardExitTimer); stopStatusTimeout(); }) - .catch((err) => { + .catch((err: unknown) => { if (!isTuiTerminalLossError(err)) { try { process.stderr.write(`openclaw tui shutdown failed: ${String(err)}\n`); @@ -1506,7 +1506,7 @@ export async function runTui(opts: RunTuiOptions): Promise { } updateFooter(); tui.requestRender(); - })().catch((err) => { + })().catch((err: unknown) => { chatLog.addSystem(`startup failed: ${String(err)}`); setConnectionStatus("startup failed", 5000); tui.requestRender(); diff --git a/src/utils/fetch-timeout.test.ts b/src/utils/fetch-timeout.test.ts index a921bdf32dc..7e080dc265b 100644 --- a/src/utils/fetch-timeout.test.ts +++ b/src/utils/fetch-timeout.test.ts @@ -152,7 +152,11 @@ describe("buildTimeoutAbortSignal", () => { reject(new Error("missing signal")); return; } - signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + signal.addEventListener( + "abort", + () => reject(toLintErrorObject(signal.reason, "Non-Error rejection")), + { once: true }, + ); }), ); @@ -264,3 +268,17 @@ describe("buildTimeoutAbortSignal", () => { } }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/test/proof/thinking-signature-real-proof.ts b/test/proof/thinking-signature-real-proof.ts index 0466d427659..4ae25f942a7 100644 --- a/test/proof/thinking-signature-real-proof.ts +++ b/test/proof/thinking-signature-real-proof.ts @@ -564,7 +564,7 @@ async function main(): Promise { } } -main().catch((err) => { +main().catch((err: unknown) => { console.error("Proof script error:", err); process.exit(1); }); diff --git a/test/scripts/pnpm-audit-prod.test.ts b/test/scripts/pnpm-audit-prod.test.ts index 026106df576..6f0a0725bd0 100644 --- a/test/scripts/pnpm-audit-prod.test.ts +++ b/test/scripts/pnpm-audit-prod.test.ts @@ -240,9 +240,16 @@ snapshots: fetchImpl: ((_url, init) => { signal = init?.signal ?? undefined; return new Promise((_resolve, reject) => { - signal?.addEventListener("abort", () => reject(signal?.reason ?? new Error("aborted")), { - once: true, - }); + signal?.addEventListener( + "abort", + () => + reject( + toLintErrorObject(signal?.reason ?? new Error("aborted"), "Non-Error rejection"), + ), + { + once: true, + }, + ); }); }) as typeof fetch, }); @@ -339,3 +346,17 @@ snapshots: } }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/test/scripts/real-behavior-proof-check.test.ts b/test/scripts/real-behavior-proof-check.test.ts index 193a6917bb4..4e6354b54c3 100644 --- a/test/scripts/real-behavior-proof-check.test.ts +++ b/test/scripts/real-behavior-proof-check.test.ts @@ -62,7 +62,9 @@ describe("real-behavior-proof-check GitHub lookups", () => { it("aborts stalled proof comment fetches", async () => { const fetch = vi.fn((_url: URL, init: RequestInit) => { return new Promise((_resolve, reject) => { - init.signal?.addEventListener("abort", () => reject(init.signal?.reason)); + init.signal?.addEventListener("abort", () => + reject(toLintErrorObject(init.signal?.reason, "Non-Error rejection")), + ); }); }); @@ -177,3 +179,17 @@ describe("real-behavior-proof-check GitHub lookups", () => { ).toEqual(["100:1", "1:1", "1:2", "1:3"]); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/test/scripts/real-behavior-proof-policy.test.ts b/test/scripts/real-behavior-proof-policy.test.ts index de2b2ad7b5e..1b10e4aa860 100644 --- a/test/scripts/real-behavior-proof-policy.test.ts +++ b/test/scripts/real-behavior-proof-policy.test.ts @@ -309,27 +309,24 @@ describe("real-behavior-proof-policy", () => { "did not test.", "could not test", "could not test.", - ])( - "fails external PRs with fenced missing-proof field values: %s", - (missingProof) => { - const evidence = [ - "Terminal transcript:", - "```text", - "$ openclaw gateway status", - "discord ready", - "```", - ].join("\n"); - const notTested = ["```text", missingProof, "```"].join("\n"); + ])("fails external PRs with fenced missing-proof field values: %s", (missingProof) => { + const evidence = [ + "Terminal transcript:", + "```text", + "$ openclaw gateway status", + "discord ready", + "```", + ].join("\n"); + const notTested = ["```text", missingProof, "```"].join("\n"); - const evaluation = evaluateRealBehaviorProof({ - pullRequest: externalPr(proofBody(evidence, { notTested })), - }); + const evaluation = evaluateRealBehaviorProof({ + pullRequest: externalPr(proofBody(evidence, { notTested })), + }); - expect(evaluation.status).toBe("missing"); - expect(evaluation.missingFields).toEqual(["notTested"]); - expect(labelsForRealBehaviorProof(evaluation)).toEqual([NEEDS_REAL_BEHAVIOR_PROOF_LABEL]); - }, - ); + expect(evaluation.status).toBe("missing"); + expect(evaluation.missingFields).toEqual(["notTested"]); + expect(labelsForRealBehaviorProof(evaluation)).toEqual([NEEDS_REAL_BEHAVIOR_PROOF_LABEL]); + }); it("fails external PRs whose proof is only tests, mocks, snapshots, lint, typecheck, or CI", () => { const evaluation = evaluateRealBehaviorProof({ @@ -580,7 +577,9 @@ describe("isMaintainerTeamMember", () => { it("aborts stalled membership fetches", async () => { const fetch = vi.fn((_url: string, init: RequestInit) => { return new Promise((_resolve, reject) => { - init.signal?.addEventListener("abort", () => reject(init.signal?.reason)); + init.signal?.addEventListener("abort", () => + reject(toLintErrorObject(init.signal?.reason, "Non-Error rejection")), + ); }); }); @@ -645,3 +644,17 @@ describe("readBoundedGitHubApiJson", () => { }); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/test/scripts/resolve-openclaw-package-candidate.test.ts b/test/scripts/resolve-openclaw-package-candidate.test.ts index d35ffc78341..5fcd2344067 100644 --- a/test/scripts/resolve-openclaw-package-candidate.test.ts +++ b/test/scripts/resolve-openclaw-package-candidate.test.ts @@ -509,7 +509,7 @@ describe("resolve-openclaw-package-candidate", () => { await new Promise((resolve, reject) => { execFile("tar", ["-czf", tarball, "-C", dir, "package"], (error) => { if (error) { - reject(error); + reject(toLintErrorObject(error, "Non-Error rejection")); return; } resolve(); @@ -521,3 +521,17 @@ describe("resolve-openclaw-package-candidate", () => { ); }); }); + +function toLintErrorObject(value: unknown, fallbackMessage: string): Error { + if (value instanceof Error) { + return value; + } + if (typeof value === "string") { + return new Error(value); + } + const error = new Error(fallbackMessage, { cause: value }); + if ((typeof value === "object" && value !== null) || typeof value === "function") { + Object.assign(error, value); + } + return error; +} diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index 126b578ceec..ee6f2c29907 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -657,7 +657,7 @@ export function connectGateway(host: GatewayHost, options?: ConnectGatewayOption ...(abort.agentId ? { agentId: abort.agentId } : {}), }, ) - .catch((err) => { + .catch((err: unknown) => { // Log to console for diagnostics; user sees no feedback for a stale abort // since the run likely completed during the disconnect window anyway. console.warn("[openclaw] pending abort failed:", err); diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 8e90c2641d8..29502cc3f24 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -167,7 +167,6 @@ function resolveSidebarUnavailableReason( return "Full content is unavailable because the stored transcript entry is too large to return safely."; case "not_visible": return "Full content is unavailable because this transcript entry does not have a visible WebChat projection."; - case "not_found": default: return "Full content is no longer available for this transcript entry."; } diff --git a/ui/src/ui/chat/realtime-talk-shared.ts b/ui/src/ui/chat/realtime-talk-shared.ts index 836042b5907..32428974dc5 100644 --- a/ui/src/ui/chat/realtime-talk-shared.ts +++ b/ui/src/ui/chat/realtime-talk-shared.ts @@ -329,7 +329,7 @@ function waitForChatResult(params: { settleResolve("OpenClaw finished with no text."); }, EMPTY_FINAL_FALLBACK_GRACE_MS); }) - .catch((error) => { + .catch((error: unknown) => { settleReject(error instanceof Error ? error : new Error(String(error))); }); }; diff --git a/ui/src/ui/embed-sandbox.ts b/ui/src/ui/embed-sandbox.ts index 232ffa63f58..b7ff77f576b 100644 --- a/ui/src/ui/embed-sandbox.ts +++ b/ui/src/ui/embed-sandbox.ts @@ -8,7 +8,6 @@ export function resolveEmbedSandbox(mode: EmbedSandboxMode | null | undefined): return ""; case "trusted": return "allow-scripts allow-same-origin"; - case "scripts": default: return "allow-scripts"; }