refactor(errors): surface 57 deferred catch-and-swallow sites via logger

The earlier sweep cleaned the hot path (worker/runtime/automation/api-
server/ipc core — 59 sites). This pass finishes the deferred long tail:
main/* services, database/* CRUD modules, api-server/* routes, ipc-*
handlers, thread-integration, automation helpers, browser, skills.

Default strategy: catch (error) { logger.warn("[module] context:", ...) }
— preserve current behavior, make the failure visible instead of
silently hidden. Widened 3 logger interface signatures to include warn
where callers only passed info/error previously (thread-helpers,
host-environment-service, ipc-domain-common).

Legitimate boundaries left intact: JSON.parse on untrusted input, DNS
resolver no-records paths, explicit safe* helpers (safeJson, parseJson,
parseTriggerDetail), URL-parse predicates, and `<unserialisable>`
fallbacks inside the prompt-builder summarisers. Each kept catch now
either has a reason in context or is a documented safe* helper.

Tests adjusted only where test-constructed no-op loggers needed a warn
stub (message-dispatch, company-mode-switch, ipc-agent-graph-handlers).

With the earlier pass, AgentCompany no longer has silently-swallowing
catch blocks in main-process or renderer business code.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-17 15:25:13 -04:00
1 parent ecd1bc2e33
commit 40040b3dd2
46 files changed
+209 -113

No files matched your search

+8 -6
View File
@@ -151,7 +151,8 @@ export function executeAutomationAction(
try {
const sender = db.getAgent(fromAgentId);
channelTargetId = sender.department ?? null;
} catch {
} catch (err) {
logger.warn(`[automation] getAgent failed for sender ${fromAgentId}; dropping department target:`, err instanceof Error ? err.message : String(err));
channelTargetId = null;
}
}
@@ -221,21 +222,22 @@ export function executeAutomationAction(
let agentName = "an agent";
try {
agentName = db.getAgent(targetAgentId).name;
} catch {
// Fall back to the generic label when the agent was deleted mid-flight.
} catch (err) {
logger.warn(`[automation] getAgent failed for escalation target ${targetAgentId}; using generic label:`, err instanceof Error ? err.message : String(err));
}
let taskTitle = ctx.taskId ?? "unknown";
try {
if (ctx.taskId) taskTitle = db.getTask(ctx.taskId).title;
} catch {
// Keep the fallback title when the task no longer exists.
} catch (err) {
logger.warn(`[automation] getTask failed for escalation task ${ctx.taskId}; using id as title:`, err instanceof Error ? err.message : String(err));
}
// Heartbeat tasks are internal system tasks — skip escalation messages to avoid flooding channels.
let isHeartbeatTask = false;
if (ctx.taskId) {
try {
isHeartbeatTask = db.getTask(ctx.taskId).isHeartbeat === true;
} catch {
} catch (err) {
logger.warn(`[automation] getTask failed for heartbeat check on ${ctx.taskId}; treating as non-heartbeat:`, err instanceof Error ? err.message : String(err));
isHeartbeatTask = false;
}
}
+11 -5
View File
@@ -1,11 +1,16 @@
import type { AppDatabase } from "../database";
import type { AutomationEventContext } from "./types";
type ConditionLogger = {
warn: (...args: unknown[]) => void;
};
export function matchesEventConfig(
db: AppDatabase,
configJson: string,
sourceDepartment: string | null,
ctx: AutomationEventContext,
logger?: ConditionLogger,
): boolean {
if (sourceDepartment && ctx.department && sourceDepartment !== ctx.department) {
return false;
@@ -21,8 +26,8 @@ export function matchesEventConfig(
const task = ctx.taskId ? db.getTask(ctx.taskId) : null;
if (task && task.priority !== conditions.priority) return false;
// If task was deleted, skip this condition instead of rejecting the rule.
} catch {
// Entity missing — treat as condition not applicable, continue matching.
} catch (err) {
logger?.warn(`[automation-conditions] getTask failed for ${ctx.taskId}; treating priority condition as not applicable:`, err instanceof Error ? err.message : String(err));
}
}
if (typeof conditions.budgetThresholdPct === "number" && ctx.agentId) {
@@ -33,8 +38,8 @@ export function matchesEventConfig(
if (utilization < (conditions.budgetThresholdPct as number) / 100) return false;
}
// If budget is 0 (unlimited), skip budget condition instead of rejecting.
} catch {
// Agent missing — treat as condition not applicable, continue matching.
} catch (err) {
logger?.warn(`[automation-conditions] getAgent failed for ${ctx.agentId}; treating budget condition as not applicable:`, err instanceof Error ? err.message : String(err));
}
}
if (conditions.taskId && ctx.taskId && conditions.taskId !== ctx.taskId) return false;
@@ -47,6 +52,7 @@ export function matchesConditions(
db: AppDatabase,
rule: { conditionsJson: string; sourceDepartment: string | null },
ctx: AutomationEventContext,
logger?: ConditionLogger,
): boolean {
return matchesEventConfig(db, rule.conditionsJson, rule.sourceDepartment, ctx);
return matchesEventConfig(db, rule.conditionsJson, rule.sourceDepartment, ctx, logger);
}
+2 -2
View File
@@ -102,8 +102,8 @@ export function handleTaskStatusChange(
}
}
}
} catch {
// Parent task may have been deleted or reassigned during completion cascade evaluation.
} catch (err) {
deps.logger.warn(`[automation] Parent task ${task.parentId} lookup failed during completion cascade:`, err instanceof Error ? err.message : String(err));
}
}
}
@@ -274,8 +274,8 @@ export function processRunCompletion(
try {
const agent = db.getAgent(run.agentId);
automationContext.department = agent.department ?? undefined;
} catch {
// Agent can disappear after a run completes; keep the context without department.
} catch (err) {
deps.logger.warn(`[automation] getAgent failed for ${run.agentId} during run completion; dropping department from context:`, err instanceof Error ? err.message : String(err));
}
if (isSuccess) {
@@ -295,8 +295,8 @@ export function processRunCompletion(
}
}
}
} catch {
// Parent task may have been deleted or reassigned during cascade evaluation.
} catch (err) {
deps.logger.warn(`[automation] Parent task ${task.parentId} cascade failed during run completion:`, err instanceof Error ? err.message : String(err));
}
}
}
@@ -326,8 +326,8 @@ export function processRunCompletion(
notify,
(en, zh) => localizeNotification(budgetLocale, en, zh),
);
} catch {
// Budget follow-up is best-effort; do not fail run completion if the agent disappeared.
} catch (err) {
deps.logger.warn(`[automation] Budget follow-up failed for agent ${run.agentId}:`, err instanceof Error ? err.message : String(err));
}
}
}
+2 -2
View File
@@ -109,7 +109,7 @@ export function createAutomationService({
try {
const rules = db.listActiveAutomationRules(ctx.companyId, trigger);
for (const rule of rules) {
if (!matchesConditions(db, rule, ctx)) continue;
if (!matchesConditions(db, rule, ctx, logger)) continue;
const executionKey = JSON.stringify({
ruleId: rule.id,
trigger,
@@ -166,7 +166,7 @@ export function createAutomationService({
try {
const workflows = db.listTriggerableWorkflows(ctx.companyId, trigger);
for (const workflow of workflows) {
if (!matchesEventConfig(db, workflow.triggerConfigJson, null, ctx)) continue;
if (!matchesEventConfig(db, workflow.triggerConfigJson, null, ctx, logger)) continue;
const executionKey = JSON.stringify({
workflowId: workflow.id,
trigger,