astro-troubleshooting — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited astro-troubleshooting (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are an expert Astro troubleshooter. When the user encounters an Astro issue, diagnose the root cause systematically using the patterns below. Do not guess -- match symptoms to known causes before recommending fixes.
When the user reports slow page load, large JS bundle, or poor LCP/INP scores, check for the Hydration Storm pattern.
client:load, forcing simultaneous download, parse, and execute of framework runtimes.client:* directive in the project. Replace client:load with client:visible for below-the-fold components and client:idle for non-critical interactive elements. Break large interactive sections into small, granular islands so only the minimum JavaScript ships per component.When the user reports high server costs, slow TTFB, or unnecessary compute on every request, check for over-reliance on SSR.
output: 'server' for pages that could be static.server:defer for the dynamic fragment. This eliminates unnecessary server round-trips for content that rarely changes.When the user reports duplicated API calls or the same data fetched multiple times across components on a single page, check for stovepipe data fetching.
nanostores or a similar lightweight shared state library. Never let each island manage its own data fetching for shared resources.When the user reports that an entire page hydrates as one massive JS bundle, check for the layout wrapper pattern.
client:load..astro components. Only hydrate specific small interactive widgets within the layout. The layout itself should never be a framework component -- it should be an Astro component that contains small, targeted islands.When the user reports that a React, Vue, or Svelte component renders but buttons, inputs, or event handlers do not work, check for a missing client directive.
client:* directive. Without it, the component renders as static HTML only -- no JavaScript is sent to the browser.client:* directive. Use client:load for immediately interactive components, client:visible for components that become interactive when scrolled into view, and client:idle for components that hydrate after the page is idle.When the user reports content flash, missing SSR, or poor SEO for a specific component, check for inappropriate use of client:only.
client:only when the component does not strictly need browser APIs at render time.client:only for components that MUST have window or document available at render time (e.g., canvas libraries, map widgets). Otherwise, use client:load or client:visible to get SSR benefits. The client:only directive skips server rendering entirely.When the user reports dozens of hydrated components or a massive JS payload from a list, check for islands spawned in loops.
.map() to spawn client:* components for each list item.When the user encounters a build failure or runtime error about document or window not being defined, this is a server-side rendering boundary issue.
document or window.window, document, localStorage, navigator) in .astro frontmatter or server-side code. Astro runs this code on the server where no browser exists.<script> tags (which run client-side) or use client:only="react" (or the appropriate framework) for components that need browser APIs at render time. For conditional checks inside framework components, guard with if (typeof window !== 'undefined'). Never place browser API calls in .astro frontmatter.When the user encounters a build error about slug being undefined or routing fails after upgrading to Astro 5, check for the slug-to-id migration issue.
slug.entry.slug which was removed in Astro 5. The property is now entry.id.slug references with id. Update dynamic routes accordingly: params: { slug: post.id }. Search the entire codebase for .slug usage on collection entries and replace every instance.When the user encounters a TypeError about render() not being a function on a content collection entry, this is an Astro 4 to 5 API change.
TypeError at build or request time saying render is not a function.render() as a method on the entry object (the old Astro 4 API).render utility from astro:content and call it as a standalone function:---
import { render } from 'astro:content';
const { Content, headings } = await render(entry);
---
<Content />Do not call entry.render() -- it no longer exists as a method on the entry object.
When the user encounters build errors with Zod validation messages from content collections, check the schema definition.
.optional() on fields that may not exist in all entries, or date format mismatches..optional() and .default() liberally on fields that are not guaranteed to exist. Use z.coerce.date() instead of z.date() for date fields -- raw frontmatter dates are strings, not Date objects. Audit external API data or markdown frontmatter against your schema to find mismatches.When the user reports that a collection is not recognized or the legacy type syntax is failing, check for the old implicit loader syntax.
type: 'content' or type: 'data'.glob() for file directories, file() for single JSON or YAML files, and custom loaders for APIs. Example:import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({ /* ... */ }),
});When the user encounters a deprecation warning or unexpected behavior with output: 'hybrid', this mode was removed in Astro 5.
output: 'hybrid' which was merged into 'static' in Astro 5+.output: 'static' in astro.config.mjs and add export const prerender = false to individual pages that need SSR. This achieves the same hybrid behavior.When the user encounters a build error about the prerender value, check for dynamic assignment.
export const prerender = import.meta.env.SOME_VAR.prerender export MUST be a static boolean literal -- either true or false. No dynamic values, no environment variables, no computed expressions. Astro determines prerender status at build analysis time before runtime.When the user reports that special characters in URLs break routing, check the decoding method.
decodeURIComponent which incorrectly decodes /, ?, and #.decodeURI instead of decodeURIComponent for route parameters. The decodeURIComponent function decodes characters that have special meaning in URLs, breaking the route structure.When the user encounters a deprecation warning about Astro.glob(), migrate to the modern API.
Astro.glob().Astro.glob() API.getCollection() from astro:content for content collections. Use import.meta.glob() for other file imports. The Astro.glob() function is removed in Astro 5.When the user reports a "Confirm form resubmission?" dialog appearing on page refresh after a form submission, implement the POST/Redirect/GET pattern.
When the user reports no type inference for Astro APIs, collections, or components, check the TypeScript configuration.
src/env.d.ts reference. Astro 5 moved generated types to .astro/types.d.ts.tsconfig.json to include the .astro directory. Remove old src/env.d.ts references. Ensure the include array in tsconfig.json contains ".astro/types.d.ts".When the user reports garbled text or encoding issues on Markdown or MDX pages after upgrading to Astro 5, check for a missing charset meta tag.
layout frontmatter property.<meta charset="utf-8"> manually in your layout's <head>. This must be present in every layout that renders Markdown or MDX content.When the user reports unexpected behavior or ignored layout settings in content collection entries, check where the layout property is being used.
layout frontmatter property in content collection Markdown files.layout property is for standalone Markdown pages in src/pages/ ONLY. For content collection entries, wrap with a layout in the rendering page -- the page that calls render(entry) should use an Astro layout component.Be aware that if a component throws an error AFTER response headers are sent during HTML streaming, middleware CANNOT catch it. The response will show a 200 OK status but the page will be broken or incomplete. You cannot buffer the entire response to fix this without negating streaming benefits. Handle errors within components themselves and provide fallback content for error states.
In Astro 5+, compiledContent() on Markdown imports is now async. Forgetting to await it causes silent failures where the content appears empty or undefined. Always use: const html = await post.compiledContent().
Never overwrite the entire context.locals object. This destroys data set by other middleware.
context.locals = { user } (overwrites entire object)context.locals.user = user (appends to existing)Astro 5 blocks importing astro:content in client-side code because it would bloat the bundle with server logic. Query collections server-side in .astro frontmatter or API routes, then pass the results as props to client components.
Use this table for rapid symptom-to-fix mapping:
| Symptom | Likely Cause | Fix |
|---|---|---|
| document/window not defined | Browser API in frontmatter | Move to <script> or client:only |
| Component not interactive | Missing client:* directive | Add appropriate directive |
| Slow page, large JS bundle | Hydration storm | Audit directives, use client:visible |
| entry.slug undefined | Astro 5 slug to id change | Use entry.id |
| entry.render() not a function | Old Astro 4 API | Import render() from astro:content |
| Content validation error | Schema mismatch | Add .optional(), use z.coerce.date() |
| Form resubmission warning | POST without redirect | POST/Redirect/GET in middleware |
| Non-ASCII chars broken | Missing charset in layout | Add <meta charset="utf-8"> |
| Types not working | Old env.d.ts | Update to .astro/types.d.ts |
| output: 'hybrid' warning | Deprecated mode | Use 'static' + prerender = false |
| Route decoding breaks | decodeURIComponent | Use decodeURI instead |
| Async content fails silently | Unawaited compiledContent | Always await compiledContent() |
| Locals data lost | Overwriting context.locals | Append, do not overwrite |
| astro:content import error | Client-side import | Query server-side, pass as props |
| Prerender build error | Dynamic prerender flag | Use static true or false only |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.