573bd63e — "[v15.5.0-rc5] use axis_order default setting"

Hi @gabrielburnworth,

I’d like to report a bug introduced in commit 573bd63e ([v15.5.0-rc5] use axis_order default setting) in lib/celery/compilers/move_compiler.ex that causes all move commands to fail on self-hosted installations.


Environment

  • FarmBot OS: 15.5.1
  • Hardware: Raspberry Pi 3 (rpi3)
  • Firmware: Farmduino v1.6 (farmduino_k16)
  • Web app: self-hosted, master branch (updated 2026-05-16)
  • Database: PostgreSQL 16
  • Deployment: Docker Compose

Problem

Every move command fails immediately — whether triggered via the map “GO” button, manual coordinate input, or a sequence containing a move node. Homing, Lua scripts, and photo sequences work fine. Only movement is affected.

Error message (from device logs)

Failed to execute command: %FunctionClauseError{
  module: String,
  function: :split,
  arity: 3,
  kind: nil,
  args: nil,
  clauses: nil
}

This error appears consistently every time any move command is executed, regardless of the coordinate values provided (tested with explicit X, Y, Z values as well as map clicks).


Root cause analysis

Step 1 — Identifying the CeleryScript payload

We captured the exact payload the web app sends to the device for a simple move sequence (X:100, Y:100, Z:-10):

{
  "kind": "move",
  "args": {},
  "body": [
    {"kind": "axis_overwrite", "args": {"axis": "x", "axis_operand": {"kind": "numeric", "args": {"number": 100}}}},
    {"kind": "axis_overwrite", "args": {"axis": "y", "axis_operand": {"kind": "numeric", "args": {"number": 100}}}},
    {"kind": "axis_overwrite", "args": {"axis": "z", "axis_operand": {"kind": "numeric", "args": {"number": -10}}}}
  ]
}

The payload is valid — all three axes are explicitly provided with numeric values.

Step 2 — Locating the crash

In lib/celery/compilers/move_compiler.ex, the add_defaults/1 function reads default_axis_order from fbos_config:

defp add_defaults(body) do
  if Enum.any?(body, fn %{kind: k} -> k == :axis_order end) do
    body
  else
    default_order = default_axis_order()  # <-- returns nil on self-hosted

    case default_order do
      "safe_z" ->
        body ++ [%{kind: :safe_z, args: %{}}]

      _ ->
        [grouping, route] = String.split(default_order, ";")  # <-- CRASH: nil passed here
        body ++ [%{kind: :axis_order, args: %{grouping: grouping, route: route}}]
    end
  end
end

When default_axis_order is nil (which it is on self-hosted installations where the database column does not exist), String.split(nil, ";") raises a FunctionClauseError because String.split/3 does not accept nil as its first argument.

Step 3 — Confirming the missing column

The fbos_configs table on a self-hosted installation does not have a default_axis_order column — it was never added by a web app migration. Confirmed via Rails console:

ActiveRecord::Base.connection.columns("fbos_configs").map(&:name)
# => ["id", "device_id", ..., "safe_height", "soil_height", "gantry_height"]
# default_axis_order is absent

As a result, FarmbotOS.Asset.fbos_config(:default_axis_order) returns nil, and the crash follows.

Step 4 — Workaround applied

We manually added the missing column directly in the database and broadcast the updated config to the device:

ActiveRecord::Base.connection.add_column :fbos_configs, :default_axis_order, :string, default: "xyz;in_order"
FbosConfig.reset_column_information
Device.first.fbos_config.broadcast!

After the device received the updated config, move commands started working immediately:

device_1 FBOS_LOG v15.5.1 Moving to (1340, 760, 0)

Suggested fix

Option A — Fix in FBOS (defensive nil guard)

In lib/celery/compilers/move_compiler.ex, add a fallback for when default_axis_order is nil:

[grouping, route] = String.split(default_order || "xyz;in_order", ";")

This is a one-line fix that makes FBOS resilient to missing or nil config values, which is important for self-hosted users who may not always have the latest database schema.

Option B — Fix in farmbot-web-app (add migration)

Add a Rails migration to the web app that creates the default_axis_order column with a sensible default:

class AddDefaultAxisOrderToFbosConfigs < ActiveRecord::Migration[6.1]
  def change
    add_column :fbos_configs, :default_axis_order, :string, default: "xyz;in_order"
  end
end

And include the field in the FbosConfig serializer so it is broadcast to the device on sync.

Recommended: both options together

Option A protects against future cases where the config value is missing or nil. Option B ensures self-hosted users get the correct value from the server. Both together provide the most robust solution.


The bug was introduced in commit 573bd63e ([v15.5.0-rc5] use axis_order default setting, 2025-08-02) when default_axis_order was read from fbos_config without a nil fallback, and without a corresponding web app migration to guarantee the value is always present.

Happy to provide any additional logs or test data. Thank you for your work on FarmBot OS!

3 Likes

Thank you for the detailed report!

The migration does exist and should run when sudo docker compose run web bundle exec rails db:migrate is executed as part of the upgrade instructions. The default_axis_order field is serialized and will sync to the device.

The current handling of nil in FarmBot OS is intentional. The web app is the source of truth for the multiple default value options, and during the edge case where it is undefined the intention is to trigger the error rather than move in unexpected ways.

Thanks again for taking the time to write this up, it is appreciated!

1 Like

I heave to analyze why the db migration failed for me.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.