{"id":"318a5105b175d834","repo":"gohugoio/hugo","slug":"local-ref-q-not-found","errorCode":null,"errorMessage":"local ref %q not found","messagePattern":"local ref %q not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/openapi/openapi3/openapi3.go","lineNumber":194,"sourceCode":"\t\t}\n\t\tcontent, err := resources.InternalResourceSourceContent(r.ctx, res)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read remote ref %q: %w\", loc.String(), err)\n\t\t}\n\t\tr.idm.AddIdentity(identity.FirstIdentity(res))\n\t\treturn []byte(content), nil\n\t}\n\n\tvar filename string\n\tif strings.HasPrefix(loc.Path, \"/\") {\n\t\tfilename = loc.Path\n\t} else {\n\t\tfilename = path.Join(r.relBase, loc.Path)\n\t}\n\n\tres := r.ns.resourcesNs.Get(filename)\n\tif res == nil {\n\t\treturn nil, fmt.Errorf(\"local ref %q not found\", loc.String())\n\t}\n\tcontent, err := resources.InternalResourceSourceContent(r.ctx, res)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read local ref %q: %w\", loc.String(), err)\n\t}\n\tr.idm.AddIdentity(identity.FirstIdentity(res))\n\treturn []byte(content), nil\n}\n","sourceCodeStart":176,"sourceCodeEnd":203,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/openapi/openapi3/openapi3.go#L176-L203","documentation":"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.","triggerScenarios":"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/`).","commonSituations":"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.","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."],"exampleFix":"# before: file lives at static/schemas/pet.yaml\n$ref: \"./schemas/pet.yaml\"\n# after: file moved to assets/api/schemas/pet.yaml next to the spec\n$ref: \"./schemas/pet.yaml\"  # now resolvable via resources.Get","handlingStrategy":"validation","validationCode":"# validate the spec's internal $refs before building\nnpx @redocly/cli lint api/openapi.yaml","typeGuard":null,"tryCatchPattern":"{{ $r := try (openapi3.Unmarshal $spec) }}\n{{ with $r.Err }}{{ errorf \"broken $ref in OpenAPI spec: %s\" . }}{{ end }}","preventionTips":["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"],"tags":["hugo","openapi","assets","local-ref","paths"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}