Skip to content

case_sla_monitor dies on a breached case with no owner — the whole scheduled run fails, terminally #1405

Description

@os-trump

Found while browser-verifying #1078 (PR #1404) on a fresh pnpm dev boot. Out of that card's scope, filed rather than fixed.

What happened

One scheduled run of case_sla_monitor failed terminally on the first boot of a fresh database, before anyone touched the app:

ERROR Trigger-fired run of flow 'case_sla_monitor' failed (trigger 'schedule')
  — no caller holds this result and nothing retries the run; the terminal
  failure is recorded in the flow's run history …

  Node 'notify_team' failed: notify: at least one recipient is required,
  but every recipient template resolved to nothing: {currentCase.owner_id}.

src/flows/case-sla-monitor.flow.ts, the notify_team node inside the loop_cases body, addresses exactly one recipient:

recipients: ['{currentCase.owner_id}'],

The engine's own advice in that message ("address the id directly") is already what the flow does — the template is correct. What is missing is the case where the id is not there: crm_case.owner_id is nullable, an unowned case is an ordinary state (this repo ships scripts/backfill-owner-id.ts and a pnpm backfill:owner script precisely because ownerless rows happen), and a breached case with no owner leaves the node with an empty recipient slate, which the notify node treats as a hard failure.

Why it is worth a card rather than a shrug

The failure is not scoped to the one case. It is the run that fails, and the run is the scheduled sweep that flags every breached case — so the SLA monitor stops doing its job for the rest of that tick, and nothing retries it. There is no in-product signal either: the terminal failure lands in the flow's run history and the boot diagnostics, not anywhere a service manager looks.

The specific boot I saw it on had the seeder still running in the background ("Inline seed exceeded 8000ms budget for app.objectstack.hotcrm; continuing in background"), so the case the sweep picked up was very likely mid-seed with owner_id not yet stamped — by the time I queried, all 38 seeded cases had owners and none was ownerless. That makes the observed trigger a boot race, but it is not what makes it a defect: any ownerless breached case reaches the same node the same way, and that state is reachable through ordinary REST creation and import.

Not verified, for triage

  • Whether the loop aborts on the first failing iteration or continues and reports at the end — I saw one run fail, not which of the loop's cases were skipped. That decides how bad this is.
  • Whether case_escalation / case_escalation_on_create have the same hole: they use {caseRecord.owner_id} in the same single-recipient shape, so they look exposed to the same empty slate, but I did not observe them fail.

Shape of a fix, for whoever picks it up

Two candidates, and they are not equivalent:

  1. Guard the branch — extend the condition that already gates flag_breach / notify_team so the notify only runs when the case has an owner. Cheap, and it silently drops the alert for exactly the cases most likely to be neglected.
  2. Give the node a fallback recipient — a role or position (service manager) so an unowned breached case escalates to someone rather than nowhere. That is the behaviour a service org would expect, and it is a product decision, not a mechanical fix.

Recommendation: 2, with 1 as the safety net, but it wants a maintainer's call on who an ownerless breach should reach.

Measured on @objectstack/runtime 17.1.0, origin/main @ 4c6add4.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfaces

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions