gohugoio/hugo · error
local ref %q not found
Error message
local ref %q not found
What it means
For external `$ref`s without a scheme/host, Hugo resolves them as local files: absolute paths from the assets root, relative paths joined against the directory of the spec resource, then looked up with `resources.Get` (tpl/openapi/openapi3/openapi3.go:192-194). If no asset exists at the computed path, resolution fails with the ref string.
Source
Thrown at tpl/openapi/openapi3/openapi3.go:194
}
content, err := resources.InternalResourceSourceContent(r.ctx, res)
if err != nil {
return nil, fmt.Errorf("failed to read remote ref %q: %w", loc.String(), err)
}
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 8a468df065)
Solutions
- Place the referenced files under `assets/` at the path the ref resolves to, relative to the spec's own directory.
- Fix the `$ref` path (and case) to match the actual file location; use an absolute-from-assets path like `/api/schemas/pet.yaml` if relative resolution is ambiguous.
- Ensure the main spec itself is loaded from `assets/` via `resources.Get` so relative refs resolve against its directory.
- Alternatively bundle the spec into a single file (e.g. with an OpenAPI bundler) to eliminate local refs.
Example fix
# before: file lives at static/schemas/pet.yaml $ref: "./schemas/pet.yaml" # after: file moved to assets/api/schemas/pet.yaml next to the spec $ref: "./schemas/pet.yaml" # now resolvable via resources.Get
Defensive patterns
Strategy: validation
Validate before calling
# validate the spec's internal $refs before building npx @redocly/cli lint api/openapi.yaml
Try / catch
{{ $r := try (openapi3.Unmarshal $spec) }}
{{ with $r.Err }}{{ errorf "broken $ref in OpenAPI spec: %s" . }}{{ end }} Prevention
- Lint the OpenAPI document (redocly/spectral) in CI so dangling #/components refs fail before Hugo does
- Keep $ref pointers in sync when renaming schemas/components
- Refs are case-sensitive JSON pointers — match component names exactly
- When splitting a spec across files, confirm every local ref resolves in the merged document
When it happens
Trigger: A spec in `assets/api/openapi.yaml` containing `$ref: "./schemas/pet.yaml"` when `assets/api/schemas/pet.yaml` doesn't exist; refs with wrong relative paths, wrong case, or pointing outside the assets directory (e.g. into `static/`).
Common situations: Referenced schema files kept in `static/` or the project root instead of `assets/`, path case mismatches surfacing on Linux CI, relative refs written for a different base directory than where the spec resource actually lives, or the spec loaded via a path where `relBase` can't be computed.
Related errors
- must provide a Resource and optionally an options map
- no Key set in Resource
- MIME %q not supported
- failed to get remote ref %q: %w
- failed to load image config: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/318a5105b175d834.json.
Report an issue: GitHub ↗.