If you self-host, your error workflow can be configured, visible in Settings, and still never run. I went looking for how that happens in the n8n source. Everything below is from the n8n@2.40.5 tag, in packages/cli/src/workflows/workflow-execution.service.ts and packages/cli/src/executions/execution-recovery.service.ts.
1. The error workflow is not published. In executeErrorWorkflow, before anything runs, there is a check on loaded.activeVersion === null which calls this.logger.error(notActiveError, { workflowId }) and then returns null. The message reads Calling Error Workflow for "X". Workflow "Y" is not active and cannot be executed. It logs and returns. No failed execution, nothing in the executions list, no notification. Worth noting the check is on activeVersion, not the older active column, so if you audit this yourself, audit the field n8n actually reads.
2. The error workflow has no Error Trigger node. The same method walks the nodes looking for n8n-nodes-base.errorTrigger. If it is not there you get Could not find "n8n-nodes-base.errorTrigger" in workflow "Y" and the same silent return. A workflow that starts with a Webhook or a Schedule Trigger is a perfectly valid workflow and a completely inert error workflow.
3. The execution crashed rather than failed. This one surprised me. When n8n restarts, ExecutionRecoveryService.amend() marks whatever was in flight as crashed, then calls runHooks, which fires workflowExecuteAfter and does call executeErrorWorkflow. So your alert is not lost. It arrives at the next startup, not at the time of the failure. If a process never comes back, nothing ever fires, because the code that reports the failure lives inside the process that died.
What this looked like on a real instance. I set up a workflow that throws on a one-minute schedule, wired it to an error workflow, confirmed alerts were arriving, then unpublished the error workflow and left everything running. Over the next fifteen minutes n8n recorded failure after failure, once a minute. Zero notifications. The only evidence anywhere was the log line from point 1. From the outside, an instance failing every sixty seconds and an instance with nothing to do look exactly the same.
Things worth doing, in rough order of payoff.
Grep your logs for the two strings above. If either appears, you have been running without alerting for however long that line is old.
Audit which active workflows actually have an error workflow set. There is no global default in n8n, it is per workflow, so the gap grows every time you build something new. One line, using the public API and jq: request /api/v1/workflows?limit=250 with your X-N8N-API-KEY header, then filter with .data[] | select(.active==true) | select((.settings.errorWorkflow // "")=="") | .name to list the active workflows that have nothing wired up.
Treat publishing the error workflow as part of the deploy, not as a thing you do once. Anything that can be toggled off will eventually be toggled off by someone who did not know what it was for.
And do not expect the error workflow to tell you the instance is down. By construction it cannot: it runs inside the thing it is watching.
Disclosure: I am building an outside-in monitor for self-hosted n8n, which is why I had reason to read these paths. No link, and none of the above depends on it.