Skip to content

An artifact that can't be written

The book builds, and then Promoting the artifact stops with ✗. Keystone builds into scratch space and moves the finished file into artifacts/ as its last act, so this failure arrives after the build itself has succeeded.

What it looks like

Publishing target: 'book' | format: pdf
  ✓ Preparing your artifact      1.5s
  ✓ Typesetting (pass 1)         22.1s
  ✓ Typesetting (pass 2)         19.8s
  ✓ Typesetting (pass 3)         16.5s
  ✗ Promoting the artifact       0.0s
ERROR: publishing failed while promoting the artifact to
  artifacts/my-project-book-20260101.pdf
  │ mv: cannot create regular file 'artifacts/my-project-book-20260101.pdf': Permission denied
  See https://keystone.knight-owl.dev/errors/artifact-not-written/

The ERROR: is the same every time. The mv: line quoted under it carries the reason:

mv: cannot create regular file 'artifacts/…': Permission denied
mv: inter-device move failed: '/tmp/…' to 'artifacts/…'; unable to remove target: Read-only file system
mv: error writing 'artifacts/…': No space left on device
mv: cannot create regular file 'artifacts/…': No such file or directory

A strict build can stop just before this stage, with every stage ✓ and and the build produced warnings: instead. That one lists the warnings that stopped it.

What it means

The book built, and artifacts/ would not take it.

The quoted line is the one to read: that is the operating system refusing the write, reported by mv. Nothing is staged for recovery, so clear the obstruction it names and build again.

The directory isn't writable by you

Permission denied. artifacts/ exists, and the user the build runs as cannot write to it. Builds run as your own user and never as root, so a directory owned by somebody else stays closed — one Docker created for itself because artifacts/ was missing when a container started, one left behind by a build run under sudo, or one copied from another machine with its ownership intact.

Take the directory back

chmod u+w artifacts restores a cleared write bit. Where another user owns the directory, sudo chown -R "$(id -u):$(id -g)" artifacts hands it back.

The filesystem is read-only

Read-only file system. Nothing here can be written, whatever the permissions say: a project opened from a mounted disk image, a read-only network share, or container wiring edited to mount artifacts/ :ro.

The move is always across filesystems, because scratch space is in memory and artifacts/ is on disk. inter-device therefore describes every promotion, and the fault is the reason at the end of the line.

Build somewhere writable, or put the mount back

Copy the project onto a disk you can write to. Where the mount is the cause, restoring .docker/docker-compose.yaml — from git, or from a fresh copy of your template — drops the :ro. That file is the engine's to own, so nothing of yours goes with it.

The disk is full

No space left on device. The disk holding your project has no room for the book.

The move had already begun, so artifacts/ holds a truncated file under the name a finished book would carry. It opens as a damaged document, if at all.

Clear the artifacts you no longer need

make clean empties artifacts/, which is often enough on its own: a build names its output for the day it ran, so nothing is overwritten from one day to the next. Empty the directory rather than deleting it — see The directory was recreated. Where the space went elsewhere, free it there.

The directory was recreated

No such file or directory. Deleting artifacts/ and letting the next build recreate it leaves Docker serving the container the deleted directory. Listing it works, so the build finds an artifacts/ and only the write fails.

It comes and goes: catching the stale directory is a race, so the same project can fail once and publish the next time.

Empty artifacts/, never delete it

make clean empties it in place and cannot cause this. Where it has already happened, another build or two picks up the real directory — nothing is wrong with the project.