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
- Write
app/manifest.jsonfirst — datasets and settings — and run the folder locally (ardaplayer -dev .) withseed/files. Nothing needs a server yet. - 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. - Then feed the data (HTTP API or portal) and fill in the settings' values in the portal.
- Then add devices, each with its branch and, for partitioned datasets, its partition (with
"required": truein the manifest'spartitionblock, and the project's Setup set to use the app's list, a device without one is refused). - 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, soneedForBootholds 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. Readr.datawhen it is an envelope (helper). dataset()can resolvenullbefore anything has arrived. Writerows || {}.- 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 isconnected, and the'ack'event names theseqs stored and refused (Data). Up to v1.1.17 it reports neither the outbox nor whether the device is online.readymeans the boot datasets were walked since start; in a local run it is alwaysfalse. needForBooton 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.configis frozen and replaced wholesale. ReadArda.config.xwhere you use it; a variable that kept the old object keeps old values. Listen to'config'.- Absent, not null: use
??, never||, or a switched-offboolreads as your default. - Up to v1.1.16 an app cannot write its settings. From v1.1.17
Arda.config.setwrites this device's own value of a setting whose manifest allows it; feature-check withtypeof Arda.config.set === 'function', and write only onceArda.confighas values, or the write comes backSTALE(Settings). - A setting's
defaultseeds 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
rowsfield with"secret": true(the only placesecretexists); 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
listsetting 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.jsonis for the desk only. Outside-devthe 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/, andapp/is packed exactly as it is: no build step runs, a symbolic link fails the pack, dotfiles are left out. - Keep
state/,data/andstorage/out of git;state/holds a desk device's private key. - The
webhookslist inarda.dev.jsonis used even with"device": true.
Hardware
- A camera's login goes in
Arda.camera.set'suserandpassword, never in the link. Never callonvif()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) andcss; set custom properties on thekeyboardelement, not on:host. Yourhtml { font-size }does not size it (since v1.1.12). Only the PIN layout listens to a physical keyboard.nullis cancel. - Barcode scanners also type into the focused field.
- Printer text is a code page, not UTF-8; check
usable;confirmed: falsemeans 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.onhas nooff(): register listeners once.'device'fires when the connection to ArdaForm changes only from v1.2.0.- Headless has no DOM (
documentthrows) 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 see | Look 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 time | The datasets and files the app waits for are still downloading. Mark fewer with needForBoot. |
| The app runs, lists are empty | In 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 portal | The 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 not | The app read Arda.config once. Is the device overriding it (perDevice)? Evaluate Arda.config in Device view. |
| The deploy did not happen | The Code and Connectors tabs: a manifest fault fails the pack and lists every fault. |
No console output in the portal | Log capture is off. |
| The player refuses to start on your desk | An unknown key in arda.dev.json, or you ran without -dev beside one. The message names it. |
| The keyboard is tiny, or ignores your CSS | The shadow root (above). Use theme.unit to size it. |
| A camera is black | Arda.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.