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

  1. Verify the referenced asset file actually exists under the assets/ (or layouts/) directory and is readable.
  2. 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.
  3. Avoid symlinks for referenced OpenAPI fragment files since hugofs drops them; copy the files instead.
  4. 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

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


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