gohugoio/hugo · error
failed to read local ref %q: %w
Error message
failed to read local ref %q: %w
What it means
Thrown by the OpenAPI v3 document loader when resolving an external $ref that points to a local file in the Hugo assets filesystem. The resource was found (so the path is valid) but InternalResourceSourceContent failed to read its bytes. The %q placeholder is the ref location and %w wraps the underlying read error.
Source
Thrown at tpl/openapi/openapi3/openapi3.go:198
}
r.idm.AddIdentity(identity.FirstIdentity(res))
return []byte(content), nil
}
var filename string
if strings.HasPrefix(loc.Path, "/") {
filename = loc.Path
} else {
filename = path.Join(r.relBase, loc.Path)
}
res := r.ns.resourcesNs.Get(filename)
if res == nil {
return nil, fmt.Errorf("local ref %q not found", loc.String())
}
content, err := resources.InternalResourceSourceContent(r.ctx, res)
if err != nil {
return nil, fmt.Errorf("failed to read local ref %q: %w", loc.String(), err)
}
r.idm.AddIdentity(identity.FirstIdentity(res))
return []byte(content), nil
}
View on GitHub (pinned to 52c9bd7908)
Solutions
- Verify the referenced asset file actually exists under the assets/ (or layouts/) directory and is readable.
- Check the $ref path is relative to the OpenAPI document location, not the project root; the resolver joins r.relBase with loc.Path for non-absolute paths.
- Avoid symlinks for referenced OpenAPI fragment files since hugofs drops them; copy the files instead.
- Inspect the wrapped %w error to identify whether it is a permissions, missing-content, or fetch issue.
Example fix
// before (broken $ref path): openapi: "$ref": "user.yaml" // after (path relative to the spec's own directory): openapi: "$ref": "components/user.yaml"
Defensive patterns
Strategy: validation
Validate before calling
{{ if and (resources.Get $refPath) (not (os.FileExists $refPath)) }}
{{ erroridf "openapi-ref" "ref target unreadable: %q" $refPath }}
{{ end }} Type guard
// Go-side guard before building the doc:
if r, ok := ns.Get(refPath); ok && r != nil {
if _, err := resources.InternalResourceSourceContent(ctx, r); err != nil { /* skip / warn */ }
} Prevention
- Keep all $ref fragment files as real files (not symlinks) under assets/.
- Validate that every local $ref target resolves to a readable resource before publishing.
- Pin the OpenAPI source under version control so ref targets cannot drift.
When it happens
Trigger: Calling openapi3.Unmarshal on a Resource whose OpenAPI document contains a $ref (e.g. "$ref": "components/user.yaml") pointing to another asset file whose content cannot be read at resolution time. Occurs during ResolveRefsIn when IsExternalRefsAllowed is true.
Common situations: The referenced asset file is a symlink that was dropped by hugofs.DropSymlinksFs, the resource content is a remote-backed resource whose fetch hasn't completed, the file was deleted/renamed between build passes, or the resource's ReadSeekCloser returns an error. Also seen after upgrading Hugo versions that changed asset path resolution.
Related errors
- local ref %q not found
- target path "{targetDir}" exists but is not a directory
- target path "{targetDir}" exists and is not empty
- {jekyllRoot} is not a directory
- abort: jekyll root contains neither posts nor drafts
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/f6c8d4652aa938a4.
Report an issue: GitHub.