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.
Related
- Publishing your book — where artifacts land, and how a build reports its stages.
- Project anatomy — what lives in
artifacts/, and what the engine owns. - Sandboxing — why
artifacts/is the only writable path you own.