Skip to content

An artifact that can't be written

Every stage reports ✓ and no book lands. 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
mv: cannot create regular file 'artifacts/my-project-book-20260101.pdf': Permission denied
ERROR: publishing failed while promoting the artifact to
  artifacts/my-project-book-20260101.pdf
  See https://keystone.knight-owl.dev/errors/artifact-not-written/

The ERROR: is the same every time. The mv: line above 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 reaches the same all-✓ shape by another route and stops with 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 line above the ERROR: is the one to read: that is the operating system refusing the write, quoted 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.