intermediate
work-orders
troubleshooting
support

Troubleshooting Work Orders

Common Levy Service problems and how to diagnose them: stuck tasks, missing rules, offline sync conflicts, and verify gating

Levy Fleets TeamMay 18, 20266 min read

Troubleshooting Work Orders

When something looks wrong on the work order board, the cause is almost always one of the patterns below. Walk this list before opening a support ticket.

A task is stuck in Created and the rule should have created it

Three things to check:

  1. Is the rule enabled? Open /dashboard/task-rules and confirm the toggle is on. Disabled rules don't fire.
  2. Did the cron actually run? Look at task_rule_runs for the rule's most recent entry. If it's older than the rule's schedule, the cron didn't fire — check vercel.json for the cron config and your Vercel deployments page for failures.
  3. Did the rule match zero vehicles? Hit the Test button on the rule. If the dry-run returns zero matches, your trigger config is too narrow.

The vehicle did not flip to maintenance when I created a critical task

Two causes:

  • Active ride — The auto-flip trigger refuses to interrupt a ride. If vehicles.status = 'in_use', the flip waits for the ride to end. The task is still created; the vehicle just won't go to maintenance until ride close.
  • Priority is below high — Only high and critical priorities trigger the flip. If you set medium, the vehicle stays available.

A tech says they can't tap Start

The Start button is photo-gated. The tech must capture or attach a before photo first. If they're tapping in offline mode, the photo upload may be queued — they need to see the photo in the "Photos" section of the task detail before the button enables.

If they have a photo attached and the button is still gray, check:

  • Are they the current assignee? If a manager re-assigned the task while the tech wasn't looking, only the new assignee can press Start.
  • Is the task in assigned state? If it's created (unassigned), the button is hidden.

A tech tried to Resolve and got "already closed by ___"

This is the offline-queue conflict resolution at work. The tech resolved the task offline, but the manager force-closed it server-side first. The client sees 409 on sync and shows the message. The tech's photos are still saved to the closed task (client-wins on photos and notes), so no evidence is lost.

Verify button is gray for a manager

Two reasons:

  1. Task is not in resolved state — verify requires resolve first. The state machine refuses to skip.
  2. Manager role is wrong — Only ops_manager and lead_tech can verify. A regular tech or junior_tech cannot, even on their own work.

Duplicate tasks for the same vehicle

The system blocks duplicates of the same task_type, not across types. So a vehicle can have one open repair, one open battery_swap, and one open cleaning simultaneously. That's intentional.

If you're seeing duplicates of the same type, check:

  • One is verified but not closedverified tasks are still considered closed for dedup purposes. If you're seeing both, refresh; one of them is on its way to closed.
  • A rule fired before the existing task was loaded — race condition, very rare. Manually close the duplicate.

Tasks are not getting auto-assigned by proximity

assign_by_proximity reads users.last_lat and users.last_lng to pick the nearest tech. Two things to check:

  1. Are your techs sharing location? The operator-app posts to /api/operator/location every few minutes when foregrounded. If a tech has location services off, they're invisible to proximity assignment.
  2. Are the columns populated? Query SELECT last_lat, last_lng, last_lat_at FROM users WHERE id IN (your tech IDs). If last_lat_at is older than an hour, that tech won't be picked.

The fallback when no tech is in range is to leave the task unassigned (Created column) for any tech to claim.

Three things to check:

  1. Email delivery — Magic-link emails go through Resend. Check the Resend dashboard for delivery status and bounce notices.
  2. Vendor row missing emailvendors.contact_email is the destination. If it's NULL, no email is sent.
  3. Token already used — Magic links are 7-day, single-task tokens. If the vendor already accepted, the link is no longer "new" — they should bookmark the URL after first open.

Photos won't upload from the operator-app

Photos cache locally first at ${FileSystem.documentDirectory}task-photos/<task_id>/ and upload on reconnect. If a tech reports that photos are "stuck":

  1. Check the upload progress bar on the task detail screen
  2. Force a sync from Settings → Sync Pending Items in the operator-app
  3. If that fails, check the device's Supabase Storage permissions — the task-photos bucket requires authenticated upload

The fallback is to text the photos to the ops manager who can attach them from the dashboard.

"Cannot create task — duplicate" error from the API

The /api/tasks POST returns 200 with skipped_duplicate: true when a same-type open task exists. This is a feature, not a bug. To create a second task against the same vehicle:

  • Close the first task, or
  • Use a different task_type, or
  • Wait until the first is verified (which counts as closed for dedup)

SLA breach notifications are too noisy

Three knobs:

  1. Loosen low/medium SLAs — if low is set to 24h and you have 200 low-priority tasks, you're going to breach a lot. The defaults are 7 days for low, 72h for medium.
  2. Turn off Slack on low-priority rules — set action_config.notify_slack: false on rules that don't deserve Slack.
  3. Wait for batching — Expo push batches breaches into digests at 5-in-10-minutes. Slack doesn't batch.

When to escalate to support

If a task or rule looks broken and none of the above applies, file a ticket through your CSM with the task ID and the timestamp. We can pull the task_rule_runs audit and the relevant cron logs.