Appearance
date: 2026-06-14 tags: [error-handling, todoist, observability, never-silently-skip] status: graduated graduated_to: .claude/rules/php-laravel.md
A caught external-service failure must surface — never a silent sentinel
Symptom — PR review flag on ReconcileOccasionTasks::openTodoistIds: a catch (\Throwable) { return [[], false]; } around getAllTasks() returned a safe "no answer" (so the delete-backstop correctly skipped) but reported nothing. A reconcile run that couldn't reach Todoist looked completely clean.
Root cause — the catch conflated keeping the run alive (good — return a safe fallback) with staying silent (bad — the maintainer never learns Todoist was down, or that a person-delete left an orphaned task). CLAUDE.md already sets "Google auth failure → never silently skip"; it just hadn't been generalised to other external reads.
Fix (fd849e1) — surface the failure, keep the safe fallback:
ReconcileOccasionTaskssetstodoistUnreachableon the run summary → folded intoneedsAttention()→ the daily job pushes a SystemError.RetireOccasionTasksForPersonpushes once if any retirementdeleteTaskfailed, instead of onlyLog::warning-ing.- Catches that already funnelled into
summary->errors(and the UI toasts) were left alone — they already surface.
Guard — .claude/rules/php-laravel.md: a catch around an external call may return a sentinel, but the failure must reach the maintainer (push / a needsAttention flag). Logging alone isn't surfacing.