gohugoio/hugo · error
failed to open source: %w
Error message
failed to open source: %w
What it means
Wrapped error from `resourceHash.init` (resource.go:668-689) when the underlying `ReadSeekCloser()` provider fails to open the resource's source bytes. Hugo needs the byte stream to compute the xxhash used as the resource identity/cache key. The error from `l.ReadSeekCloser()` at resource.go:673-675 is wrapped and returned once (guarded by `sync.Once`).
Source
Thrown at resources/resource.go:675
type isPublishedProvider interface {
isPublished() bool
}
type resourceHash struct {
value uint64
size int64
initOnce sync.Once
}
func (r *resourceHash) init(l hugio.ReadSeekCloserProvider) error {
var initErr error
r.initOnce.Do(func() {
var hash uint64
var size int64
f, err := l.ReadSeekCloser()
if err != nil {
initErr = fmt.Errorf("failed to open source: %w", err)
return
}
defer f.Close()
hash, size, err = hashImage(f)
if err != nil {
initErr = fmt.Errorf("failed to calculate hash: %w", err)
return
}
r.value = hash
r.size = size
})
return initErr
}
func hashImage(r io.ReadSeeker) (uint64, int64, error) {
return hashing.XXHashFromReader(r)
}View on GitHub (pinned to 52c9bd7908)
Solutions
- Verify the source file exists and is readable at the path Hugo resolved (`hugo config | grep resourceDir`, check perms).
- For symlinks, confirm the target is intact and within the project root.
- Re-run `hugo --gc` to clear stale cache entries that reference moved files.
- For remote resources, ensure the body factory returns a fresh reader each call (don't reuse a consumed `io.Reader`).
- If using a custom resource type, ensure its `ReadSeekCloser()` returns a non-nil error only on genuine I/O failure.
Example fix
// custom resource provider -- before
func (r *myRes) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
return r.consumedReader, nil
}
// after
func (r *myRes) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
return hugio.NewReadSeekerNoOpCloser(bytes.NewReader(r.bytes)), nil
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before hashing, verify the source path exists.
import "os"
func ensureReadable(p string) error {
f, err := os.Open(p)
if err != nil { return err }
f.Close()
return nil
} Try / catch
// Wrap resource creation; log and skip unopenable sources.
r, err := rs.NewResource(...)
if err != nil {
if strings.Contains(err.Error(), "failed to open source") {
logger.Warnf("skip missing resource %s: %v", path, err)
continue
}
return err
} Prevention
- Run `hugo --gc` before builds to drop stale cache entries.
- Keep `assets/`/`resources/` under version control or a stable mount.
- For remote resources, ensure the body factory is re-openable.
When it happens
Trigger: Any code path that triggers lazy hash computation on a Resource whose source can't be opened: missing file on disk, broken symlink, remote resource whose body is already consumed/closed, page bundle path moved after registration, or a `ReadSeekCloser` factory returning an error.
Common situations: External process deleted a `static/` or `assets/` file between Hugo startup and hash computation; symlink to NAS dropped; concurrent `hugo mod` clean removing a module asset; custom resource provider returning an error from its opener; remote resource body closed before re-read.
Related errors
- failed to calculate hash: %w
- failled to create base cache directory: %s
- failed to prune cache %q: %w
- failed to save file %q: %s
- failed to create file caches from configuration: %w
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/25dc8e4c94389f7c.
Report an issue: GitHub.