Skip to content

When something goes wrong

A failed run in your language

The interface says why a run failed in the language it shows, with its figures written that way: a destination too full for what the run would write, a drive that is not plugged in, a server that does not answer, a sign-in that was refused, access that was denied, a folder that does not exist, a full quota, a service asking for a pause, an FTP server out of logins, a run that was stopped, and a command before or after the run that failed, with what it printed below. The engine records every failure in English, so a run from an older version is said the same way. A failure it has no words for shows the engine's own English under The engine reported:, which is also what a bug report should quote. The phone's notification about a failed run does the same in English and German, the two languages its notifications come in.

The run was refused and nothing happened

Read the message; each refusal names its own reason.

"the left side lists no files at all" means exactly that. Something is not mounted, a path changed, or a credential expired. The engine will not treat an empty side as a deletion, which is the point.

"mass-delete brake" means the run would have removed more than half of what it knows about, and "mass-change brake" that it would have overwritten that much. The job is held and listed under Anomalies, where you let the change run or take it back. The sync command has nobody to ask, so it stops; run it once with -brake-percent 0 or -change-percent 0 if the change is genuinely what you wanted.

A naming collision means one side holds two files whose names differ only in case, and the other side cannot tell them apart. Rename one of them.

An FTP server says its login limit is reached

FTP server apps on a phone, and some small servers, let only a few logins in at once and turn the next one away with a message such as "Maximum login limit has been reached", "Too many users" or "I can't accept more than 2 connections as the same user". The connection test opens one login, so it works, while a preview or a run reads several folders side by side and opens more.

ArrowLoop opens at most 4 connections to an FTP target and moves at most 3 files at once on a job with an FTP side, whatever the job's transfers say. It logs out once a test, a preview or a run is over. If the server still turns a login away before anything was changed, ArrowLoop tries again with 2 connections and then with 1, and notes each step in its log. It keeps the number that worked for that server and account until ArrowLoop restarts, so the next preview or run starts with it.

ArrowLoop leaves the number alone when you have set concurrency for the target yourself. If the message still comes:

  • Close other apps that are connected to the server, such as a file manager on your computer. With them holding logins, even one more may be too many.
  • If you set concurrency under Other settings of the target, lower it, to 2 for example, or clear it to let ArrowLoop find the number. 0 means no limit.
  • Setting concurrency by hand also spares the refused logins each time ArrowLoop restarts and has to find the number again.
  • Raise the server app's own limit, if it has one.

The destination does not exist yet

That is fine. A bucket nobody has created, or a directory nobody has made, is treated as an empty side and the first run creates what it needs.

This does not weaken the refusal to believe an empty side. That check compares against the record: a side that reports nothing while the record says it held files is still refused, whether it reports nothing by listing zero objects or by not being there at all.

A server with its own certificate is refused

A self-hosted cloud often has a certificate signed by an authority you made yourself. Until the machine ArrowLoop runs on trusts that authority, the connection test ends with "x509: certificate signed by unknown authority". ArrowLoop has no switch that turns the check off, since that would let anyone between you and the server read your files. Install the authority instead, and the check passes as it should.

Install the authority's own certificate, the one that signed the server's, not the server's certificate itself. It usually comes as a .crt or .pem file.

On the phone:

  1. Copy the certificate to the phone.
  2. In Android's settings, open Security, then Encryption and credentials, Install a certificate and CA certificate. The path differs a little between phone makers, and searching the settings for "CA certificate" finds it.
  3. Confirm the warning and pick the file.
  4. Go back to ArrowLoop and test the target again.

ArrowLoop reads the installed authorities each time its engine starts and again whenever you come back to the app, so the new one counts straight away. The engine log on the trouble screen then says trusting the certificate authorities installed in Android's settings. To take an authority away again, open Trusted credentials under Encryption and credentials, switch to User and remove it there.

In the container: put the certificate in a folder, mount it and name the folder in SSL_CERT_DIR, for example -v /mnt/user/appdata/certs:/certs:ro and -e SSL_CERT_DIR=/certs. The image's own authorities stay trusted beside it. Restart the container afterwards.

On a computer: install the authority in the system's own store. On Windows that is Trusted Root Certification Authorities in the certificate manager, on macOS the System keychain in Keychain Access with Always Trust, and on Debian or Ubuntu a copy ending in .crt in /usr/local/share/ca-certificates/ followed by sudo update-ca-certificates. Then start ArrowLoop again.

Files keep copying back and forth

Two sides that never settle usually means the two are spelling one name differently and the engine is not folding them together. Check whether one side is case-insensitive and the other is not; the fold is decided from what the backends report about themselves.

If it is a small, fixed set of files, look at whether a backend can produce a hash. Where neither side can, comparison falls back to size and modification time, and a backend that rounds modification times will disagree with itself forever. Raising modWindow is the fix.

A file never syncs and is not reported as an error

Look at the postponed list; every run prints one. The usual reasons:

  • It changed within the quiet period, so it is still being written.
  • It matches an exclude pattern, including the built-in ones for half-written files. A file called notes.tmp will never travel.
  • It is a symbolic link or a special file, which are reported and left alone.
  • On Windows, another program is holding it open. Run as a service, or with administrator rights, and a plain copy is read from a shadow copy instead.

The job has not run for days

arrowloop jobs shows when each one last succeeded. If that is old while arrowloop history shows recent runs, the job is failing rather than idle, and the history carries the error.

If neither shows anything, the schedule may simply never fire: a cron expression is read in the container's own timezone, so check TZ.

The container restarts over and over

Read the container log. The startup banner and a green "IS READY" line are printed just before it starts listening, so a log without them died during startup, and the last line before the end is the reason.

The interface is empty

Confirm the API answers:

curl http://127.0.0.1:8422/api/jobs

If that returns jobs while the page stays blank, the binary was built without the interface. It says so on the page rather than rendering nothing, because a blank screen is indistinguishable from a broken one.

Where the state lives, and what happens without it

Each job's state database is named by its own state field, under /config in the container. Losing it is not destructive but it is disruptive: the job forgets what the two sides agreed on and treats every file on both sides as new, so everything that differs is copied rather than reconciled, and a deletion made while the record was missing is propagated to nobody.

If you have to start a job over, delete its state file deliberately and run a preview first. The preview will show you exactly what that costs before anything moves.