Skip to content

[Recovery Agent] Sitemap.xml runs at runtime with empty mdSlugs — public/content/ excluded from Netlify lambda #18014

@pettinarip

Description

@pettinarip

Error Summary

  • Source: grafana-logs
  • Item ID: grafana-fn-content-directory-var-task-public-content-not-found-returning-empty-slug-list
  • Level: WARN
  • Message: Content directory /var/task/public/content/ not found, returning empty slug list
  • Hit count: 292 (recent window)
  • Request example: 01KPTG6Z7AXST53NM89RZFZAYE

Emitted by src/lib/utils/md.ts:67-69 inside getPostSlugs.

Analysis

The WARN itself is benign — getPostSlugs catches ENOENT and returns [] by design. What the 292 hits reveal is that the graceful fallback is running on every request in the Netlify serverless function. Two facts combine to make this a silent SEO regression:

  1. public/content/ is intentionally excluded from the Netlify function bundle via outputFileTracingExcludes in next.config.js:240 (to keep the bundle size down). So /var/task/public/content/ does not exist at runtime.
  2. app/sitemap.ts is runtime-dynamic, not statically generated. It transitively depends on getAppsData() (via getAllPagesWithTranslationsgetDynamicIntlPagePaths), which hits the data layer. Next.js therefore serves sitemap.xml from the lambda rather than prerendering it.

As a result, every sitemap request:

  • Calls getPostSlugs("/") at runtime → ENOENT → returns [] → WARN logged
  • getAllPagesWithTranslations ends up with zero MD pages (only intl paths + dynamic app paths)
  • The emitted sitemap.xml is missing every content-driven page (/about/, /defi/, /guides/*, etc.) across all 25 locales

This also explains the companion queue item grafana-fn-error-enoent-no-such-file-or-directory-scandir-x-at-async-o-next-se (100 hits) — getVideoSlugs() in src/lib/utils/videos.ts does the same readdir against public/content/videos but does not have ENOENT handling, so it throws from the same sitemap.xml runtime invocation.

Affected Files

  • app/sitemap.ts (runtime sitemap generator)
  • src/lib/i18n/translationRegistry.ts (calls getPostSlugs("/") at line 131)
  • src/lib/utils/md.ts (emits the WARN)
  • src/lib/utils/videos.ts (related ENOENT, see companion item)
  • next.config.js (the outputFileTracingExcludes configuration that removes public/content)

Suggested Approach

Any of the following would fix the root cause — they trade off differently:

  1. Make sitemap.xml static. Split the sitemap so it does not depend on getAppsData() runtime fetches (e.g., read a build-time-serialized apps list). Add export const dynamic = 'force-static' and revalidate on a schedule. This is the highest-impact fix for SEO and removes the runtime filesystem dependency entirely.

  2. Pre-compute a slug manifest at build time. Serialize getPostSlugs("/") output to e.g. src/data/generated/md-slugs.json during the build step, then read the JSON at runtime instead of scanning public/content/. Cheapest to implement; also fixes the video ENOENT with a parallel video-slugs.json.

  3. Narrow the lambda exclusion. Keep public/content/**/*.md but exclude the heavier binary assets. Simplest diff but increases function bundle size and can regress cold start.

Confidence Assessment

Low. The WARN is not a bug — the handler is intentional. The remediation is an architectural decision about whether the sitemap should be static or runtime, and how to get the slug list into the lambda. Needs human review before a fix direction is chosen.


Opened automatically by the Recovery Agent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-triageAuto-triaged by Recovery Agentneeds triage 📥This issue needs triaged before being worked onrecovery-agentCreated by the Recovery Agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions