gohugoio/hugo · error

failed to copy to %q: %w

Error message

failed to copy to %q: %w

What it means

Thrown when copying a non-resource output file (e.g. one emitted via the copy/file loader) to the publish directory fails. Either helpers.OpenFilesForWriting or io.Copy returned an error, and the printed targetFilenames show where the write failed.

Source

Thrown at internal/js/esbuild/batch.go:716

				targetFilenames = append(targetFilenames, targetFilename)
			}

			fs := b.client.d.BaseFs.PublishFs

			if err := func() error {
				fw, err := helpers.OpenFilesForWriting(fs, targetFilenames...)
				if err != nil {
					return err
				}
				defer fw.Close()

				fr := bytes.NewReader(o.Contents)

				_, err = io.Copy(fw, fr)

				return err
			}(); err != nil {
				return nil, fmt.Errorf("failed to copy to %q: %w", targetFilenames, err)
			}
		}
	}

	p := &Package{
		id:     path.Join(NsBatch, b.id),
		b:      b,
		groups: groups,
	}

	return p, nil
}

func (b *batcher) removeNotSet() bool {
	// We already have the lock.
	var removed bool
	currentBuildID := b.buildCount
	for k, v := range b.scriptGroups {

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Check write permissions on the publish directory (often public/).
  2. Free disk space on the volume holding the publish dir.
  3. Inspect the target path printed for invalid characters or traversal; verify each multihost base path is correct.
  4. On Windows, exclude the Hugo publish dir from anti-virus scanning.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before the build, write and remove a sentinel file in the publish dir to
// confirm the process has write access.
sentinel := filepath.Join(publishDir, ".hugo-write-probe")
if err := os.WriteFile(sentinel, []byte("x"), 0o644); err != nil {
    return fmt.Errorf("publish dir not writable: %w", err)
}
_ = os.Remove(sentinel)

Try / catch

On copy failure, inspect the wrapped error for syscall.EACCES or ENOSPC and surface a targeted message (permissions vs. disk-full) instead of the generic copy error.

Prevention

When it happens

Trigger: Esbuild emits an output file that isn't recognized as a resource (no media type or not in a path group), so Hugo copies it directly to each multihost-aware target. The copy fails because the publish directory is not writable, is out of space, or the joined target path is invalid.

Common situations: publishDir (often public/) is read-only or owned by another user; CI runs Hugo as a non-privileged user; out of disk; misconfigured multihost producing invalid joined paths; on Windows an anti-virus locks the destination file.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/35302cf15f2b5a08. Report an issue: GitHub.