Levy Swap Troubleshooting
This page collects the most common Levy Swap issues and the first thing to check for each.
State of Health
A pack shows "n/a" for SoH
- Most likely: the vehicle's IoT type isn't on the supported list (
axa,manual, or unrecognized). SoH requires live battery telemetry. - Also possible: the pack has less than 24 hours of telemetry. The nightly job needs at least one full cycle of data to score a pack.
- Action: check the vehicle's IoT type on the vehicle detail page. If it's a supported IoT type, wait one more nightly run.
Every pack shows SoH 100
- Most likely: brand-new fleet — packs really are at 100. Wait a week and look for downward movement.
- Possible: the nightly job hasn't run yet on this subaccount. Check Dashboard → Swap → Battery Health for a "last updated" timestamp.
A pack's SoH dropped 20 points overnight
- Most likely: a firmware update on the vehicle changed how voltage is reported. Check the device's recent firmware version.
- Possible: a single extreme temperature exposure event during the day (very hot or very cold). The job weights temperature heavily.
- Action: open the pack's drill-down view and look at the temperature exposure chart for the last 7 days. If you see a spike, the SoH drop is real.
Predicted EoL keeps moving
- Expected: the model extrapolates from the most recent 30 days. As fade rate changes, the prediction updates. A moving prediction is healthier than a stuck one.
- Action: trust the window, not the exact date. Use it for ordering replacements, not for hard scheduling.
Pack QR Codes and Scans
A pack QR won't scan
- Most likely: dirty lens or low light. Wipe the lens and try in better light.
- Possible: the QR sticker is damaged. Mint a replacement pack (or, if the pack already has a valid record, reissue a sticker from the pack detail page).
"Pack not in vehicle" error during swap
- Cause: the QR you scanned doesn't match the pack currently assigned to the vehicle.
- Action: double-check the pack you physically have in your hand. If it's the right pack, the pack's
vehicle_idmay have been changed manually — check the pack's history.
"Pack is in use" error on the new-pack scan
- Cause: the pack you're trying to install is currently assigned to another vehicle.
- Action: scan a different pack. If you believe this is the right pack, check the audit log to find out which other vehicle still claims it (probably a swap that wasn't completed cleanly).
Pack stuck in received for weeks
- Cause: pack was received but never assigned to a station slot or installed.
- Action: from the pack detail page, transition it to
chargingand load it into a station slot. Or install it on a vehicle on the next swap.
Station Inventory
Slot count doesn't match physical reality
- Most likely: someone flipped a slot to
maintenanceand the slot grid is hiding it from "available" counts. - Action: open the station detail and look for yellow (maintenance) slots.
Slot won't accept a pack scan
- Cause: the pack is already assigned to another slot. Only one slot can hold a given pack.
- Action: the error message names the conflicting slot. Resolve by either removing the pack from the old slot first or correcting the QR.
Slots are all empty but packs are physically there
- Cause: slots were created on station setup, but no one scanned packs into them. Each pack needs to be loaded into a specific slot.
- Action: load each pack via the station detail page.
In-House Swap Workflow
Swap stuck in in_progress
- Cause: the app crashed or lost connectivity between Start Swap and Complete.
- Action: server-side, the swap session times out after 30 minutes. Start a new swap on the same vehicle once the timeout clears.
Unlock command timed out
- Cause: vehicle's cellular signal is too weak to receive the command.
- Action: wait 60 seconds and retry. For AXA-locked vehicles, fall back to the AXA e-key. For very weak signal, physically relocate the vehicle to a known-good cellular spot if possible.
Queue is empty but I know vehicles need swaps
- Cause: bounty engine and queue both use the same candidate logic. If no vehicle meets the threshold (low SoC, peak-shortfall, or SoH-rotation), the queue is empty.
- Action: check the Battery Health dashboard for vehicles with SoH below 60 — those should show "rotation recommended" and appear in the queue.
Juicer Claims and Payouts
Bounty map shows no bounties
- Most likely: the subaccount doesn't have
juicer_marketplace_enabled = true. Operators turn this on from Dashboard → Swap → Settings. - Possible: the Juicer is outside their
claim_radius_m. The map only shows bounties within radius. - Possible: no candidate vehicles in the subaccount right now.
"Already claimed" error
- Cause: another Juicer beat you to it. The atomic claim function only lets one Juicer win.
- Action: claim a different bounty.
"GPS too far from vehicle" at pickup
- Cause: pickup photo GPS is more than 200 m from the vehicle's last reported GPS.
- Action: move closer to the actual vehicle. If the vehicle just moved, the telemetry may take a minute to refresh.
"Drop outside zone"
- Cause: drop GPS is not inside any active Juicer-eligible drop zone.
- Action: relocate the vehicle into one of the operator's drop zones.
Payout stuck in queued
- Most likely: the
scheduled_fortime hasn't elapsed yet. Default delay is 24 hours after session completion. - Possible: Stripe Connect account isn't fully enabled (
payouts_enabled = false). - Action: check the payout row's
scheduled_forfirst, then the Juicer's Stripe Connect status.
Transfer failed with "insufficient balance"
- Cause: Levy's Stripe account doesn't have enough float right now.
- Expected behavior: cron retries every 15 minutes; usually transient.
Juicer says they didn't get paid but row shows paid
- Cause: the Juicer's bank takes 1–3 business days even after Stripe marks
paid. - Action: share the Stripe transfer ID with the Juicer; they can show it to their bank.
Reversal failed
- Cause: the Juicer's Stripe account doesn't have enough balance to pull from.
- Action: Levy carries the loss until offset by the next payout to the same Juicer. Operators see the unrecovered amount on the dashboard.
Crons Not Running
soh-nightlydidn't fire — check Vercel cron logs. The job runs at 03:00 UTC; if it failed, the next morning's Battery Health page won't have a fresh "last updated" timestamp.bounty-enginenot creating bounties — confirmjuicer_marketplace_enabled = trueon the subaccount and at least one candidate vehicle.bounty-expirenot freeing stale claims — should run every minute; check Vercel cron status.juicer-payoutsnot draining queue — check Stripe API status and the Stripe Connect platform balance.