Debugging balenaCloud: The /v1/update 503 Bug and the Stuck Legacy-to-Classic Conversion

What these two bugs have in common

Both have been tripping up balena users for a while. Neither is flashy. One crashes a live API call. The other is a dead UI link that looks functional. They’re worth understanding separately because the workarounds are different.

Bug 1: /v1/update returning 503 with “Cannot read properties of undefined (reading ‘force’)”

The raw HTTP exchange tells the story. The POST to /v1/update arrives with content-length: 0 — no request body at all. The balena Supervisor API’s /v1/update endpoint optionally accepts a JSON body containing a force boolean. When no body is sent, the supervisor’s request handler tries to read req.body.force, but req.body is undefined. It crashes and returns a 503 instead of safely defaulting to force: false.

This is a body-parsing bug. The fix is a single defensive check:

const force = req.body && req.body.force === true;

Instead the code assumes a body is always present. The supervisor also misuses HTTP 503 (Service Unavailable) here — this is a reproducible logic error, not a transient service overload. That misuse of 503 is tracked as a separate known issue in the balena-supervisor repository (GitHub issue #1298). It makes diagnosis harder because automated retry logic and monitoring tools treat 503 as a recoverable condition worth retrying.

Workaround

Set BALENA_SUPERVISOR_OVERRIDE_LOCK=1 as a device configuration variable in the balenaCloud dashboard. This instructs the supervisor to skip update lock checks entirely — which is what force: true in the API body was supposed to accomplish. This workaround holds even when the direct /v1/update call fails with the 503.

If you control the code making the API call, there is a second option: explicitly send a JSON body with {"force": false} or {"force": true} rather than leaving the body empty. That may prevent the body-parser from returning undefined, depending on supervisor version.

A related issue — GitHub issue #1454 — documents a separate case where newer supervisor versions receive the force flag but silently ignore it, continuing to respect update locks instead of overriding them. The BALENA_SUPERVISOR_OVERRIDE_LOCK workaround addresses both failure modes.

Bug 2: Legacy-to-classic device conversion does nothing

balenaCloud has had four fleet types: Legacy, Essentials, Classic, and Microservices. Since March 2023, new fleets can only be created as Microservices. The older types persist for existing customers, but movement between the legacy tiers is inconsistently supported.

The dashboard shows a conversion link from Legacy to Classic. Clicking it has no visible effect. The device stays on Legacy. No error, no confirmation, no API failure to trace. This appears to be a dead code path — the UI option was never removed when the underlying conversion stopped working, leaving the link in place as a silent no-op.

What actually works

The conversion that is functional is Legacy to Microservices, provided every device in the fleet runs balenaOS v2.12.0 or higher. If some devices can’t reach a recent enough OS version, the path gets harder: the balena-os/takeover tool can reprovision a device onto a new device type, but it is a more involved operation than a dashboard click and carries its own risks.

There is no dashboard shortcut for the broken Legacy-to-Classic path. Skip Classic entirely and target Microservices directly if OS versions allow it.

Filing reports that actually get fixed

Both issues are the kind that move when someone files a clear report with reproducible evidence. For the 503 bug, a packet capture showing content-length: 0 on the POST — exactly like the one in the original thread — is what a maintainer needs to reproduce it. For the legacy conversion bug, a network trace showing the silent no-op would do the same job.

The supervisor issue tracker is at github.com/balena-os/balena-supervisor/issues. For UI bugs, balena-io/balena-ui is the right repository.

Sources


Similar Posts