Troubleshooting.

Most lost hours come from doing things in the wrong order or from one of a dozen traps. They are all here, with where to look when something does not show.

Working — describes ArdaPlayer v1.2.3

The order of operations

  1. Write app/manifest.json first — datasets and settings — and run the folder locally (ardaplayer -dev .) with seed/ files. Nothing needs a server yet.
  2. Get the manifest to ArdaForm before anything else touches data. Push a commit to a connected branch (packing creates the datasets and settings) or run the folder with "device": true (it sends its manifest as it connects). Until then the datasets do not exist: the portal's Data tab is empty, the HTTP API cannot write to them, and records a device pushes are held on the device and retried — not lost, but not there.
  3. Then feed the data (HTTP API or portal) and fill in the settings' values in the portal.
  4. Then add devices, each with its branch and, for partitioned datasets, its partition (with "required": true in the manifest's partition block, and the project's Setup set to use the app's list, a device without one is refused).
  5. In the app, ask for every dataset at start-up (Arda.db.dataset(name)) and draw again on 'sync'. From v1.1.17 a device syncs every dataset its manifest declares when the app is installed or updated, so needForBoot holds the first screen until those are current, on the first start too. Up to v1.1.16 a device syncs a dataset only once the app has asked for it, so a brand-new device's first screen starts empty.

Gotchas

Data

  • A device's records are envelopes; a desk's are not. On a device each value of Arda.db.dataset() is {key, data, deviceId, seq, createdAt, updatedAt}; seed records and local echoes are stored as written. Read r.data when it is an envelope (helper).
  • dataset() can resolve null before anything has arrived. Write rows || {}.
  • Your own pushes come back only after a round trip: outbox → ArdaForm → the dataset's next sync, and only if the app reads that dataset and the record is in the device's partition. Developer mode shows them at once, which hides this. Keep what you just did in your own state.
  • Always pass a key to Arda.db.push(dataset, data, key). ArdaForm refuses a record without one (NO_KEY), and developer mode reads back only keyed records.
  • Money on an existing record waits for the outbox, not a timer. From v1.2.0 Arda.db.status() counts this device's queued records per dataset (pending), says whether it is connected, and the 'ack' event names the seqs stored and refused (Data). Up to v1.1.17 it reports neither the outbox nor whether the device is online. ready means the boot datasets were walked since start; in a local run it is always false.
  • needForBoot on none means all. With no dataset marked, every synced dataset must be current before the app starts. Mark the small ones you need.
  • Changing a device's partition or branch resyncs it: its data and files are dropped and the app starts again.
  • A record over 128 KB is refused at push. Pictures belong in files.

Settings and the manifest

  • Arda.config is frozen and replaced wholesale. Read Arda.config.x where you use it; a variable that kept the old object keeps old values. Listen to 'config'.
  • Absent, not null: use ??, never ||, or a switched-off bool reads as your default.
  • Up to v1.1.16 an app cannot write its settings. From v1.1.17 Arda.config.set writes this device's own value of a setting whose manifest allows it; feature-check with typeof Arda.config.set === 'function', and write only once Arda.config has values, or the write comes back STALE (Settings).
  • A setting's default seeds its value once, when the field is created; changing it later changes nothing an operator set.
  • No secrets in the manifest — it is in your repository and in every device's copy of the app. A password goes in a rows field with "secret": true (the only place secret exists); the device still receives it, so never log it.
  • A manifest fault fails the pack and no device is updated; the portal lists every fault. The player, by contrast, ignores keys it does not know.
  • A list setting reaches the app as an array of strings, on a device too (since ArdaForm's fix of 2026-10-19; Settings).

The folder and developer mode

  • arda.dev.json is for the desk only. Outside -dev the player refuses to start with one, and a container holding one does not open. An unknown key in it is refused; the command line wins over it.
  • The player adds nothing to app/, and app/ is packed exactly as it is: no build step runs, a symbolic link fails the pack, dotfiles are left out.
  • Keep state/, data/ and storage/ out of git; state/ holds a desk device's private key.
  • The webhooks list in arda.dev.json is used even with "device": true.

Hardware

  • A camera's login goes in Arda.camera.set's user and password, never in the link. Never call onvif() in a loop with a wrong password: cameras lock the account.
  • The keyboard is in a shadow root: your page's CSS cannot style it. Use theme (a closed list of names; others are refused with a warning) and css; set custom properties on the keyboard element, not on :host. Your html { font-size } does not size it (since v1.1.12). Only the PIN layout listens to a physical keyboard. null is cancel.
  • Barcode scanners also type into the focused field.
  • Printer text is a code page, not UTF-8; check usable; confirmed: false means the printer took the job, not that it printed.
  • A camera on a second monitor needs a PC-type device installed from the v1.1.12 images or later; a Raspberry Pi has one screen and six hardware camera decoders.
  • Arda.on has no off(): register listeners once. 'device' fires when the connection to ArdaForm changes only from v1.2.0.
  • Headless has no DOM (document throws) and no ES modules. Up to v1.1.16 an app deployed through ArdaForm is not run on a device without a screen; from v1.1.17 it is, through the headless runtime (not yet proved on a device).
  • Webhooks are called by ArdaForm's servers: an address on the shop's own network is unreachable. A 4xx or 5xx is a result, not a rejection.

Where to look when nothing shows

!
Switch log capture on first A device's warnings and your console output reach the portal only while log capture is on (the device's Alerts & log tab). It expires by itself. The device keeps its last few hundred lines, so switching it on shows what already happened.
You seeLook at
"Registered — waiting for this device's application"The branch has no connection, or the folder has no index.html: Connectors, then Code.
The boot screen with a progress bar, for a long timeThe datasets and files the app waits for are still downloading. Mark fewer with needForBoot.
The app runs, lists are emptyIn Device view, evaluate Arda.db.status() and Arda.db.dataset('name'). Empty with synced: true: the device's partition matches no record, or the dataset is empty on ArdaForm. Missing from datasets: the manifest does not declare it (up to v1.1.16: the app never asked for it).
Records pushed by devices never reach the portalThe device is offline, the dataset does not exist on ArdaForm yet (the push is held), it is "ro", or the record has no key. The device log says which; from v1.2.0 so does 'ack''s refused.
A setting changed but the screen did notThe app read Arda.config once. Is the device overriding it (perDevice)? Evaluate Arda.config in Device view.
The deploy did not happenThe Code and Connectors tabs: a manifest fault fails the pack and lists every fault.
No console output in the portalLog capture is off.
The player refuses to start on your deskAn unknown key in arda.dev.json, or you ran without -dev beside one. The message names it.
The keyboard is tiny, or ignores your CSSThe shadow root (above). Use theme.unit to size it.
A camera is blackArda.camera.status(): error, restarts. A wrong login, an H.265 stream on a Pi, a seventh camera on a Pi.

More, from the operator's side: the deployment guide's When something looks wrong.

Checked: each item is traceable to a page of this manual, where its own check is stated.