withastro/astro · error · Error
Glob patterns cannot start with `../`. Set the `base`…
Error message
Glob patterns cannot start with `../`. Set the `base` option to a parent directory instead.
What it means
The glob() loader's pattern is relative to its `base` option (the content directory by default). A pattern starting with `../` is rejected with a plain Error because it tries to walk outside the content root; the supported way to reach parent directories is to move the escape into `base`, which is validated separately.
Solutions
- Set `base` to the parent directory and keep the pattern relative to it: `glob({ pattern: '**/*.md', base: '../..' })` reaches the project root from src/content.
- Or relocate the content under the content directory so a plain relative pattern works.
- base also accepts a file URL (e.g. `import.meta.url`-relative), useful when the content sits inside a node_modules package.
Example fix
// before
const Docs = defineCollection({
loader: glob({ pattern: '../docs/**/*.md' }),
});
// after
const Docs = defineCollection({
loader: glob({ pattern: '**/*.md', base: '../..' }), // src/content -> project root
}); Defensive patterns
Strategy: validation
Validate before calling
function normalizeGlobOptions(pattern: string, base?: string) {
if (pattern.replace(/^(\.\/|!)+/, '').startsWith('../')) {
// fold the escape into base instead
const segments = pattern.split('/');
let i = 0;
while (segments[i] === '..' || segments[i] === '.' || segments[i] === '!..') i += segments[i] === '..' ? 1 : 0, segments[i] === '..' ? i++ : i;
// simpler: hand-write the base for known cases
}
return { pattern, base };
}
// pragmatic check:
if (/^(\.\/|!)*(\.\.\/)/.test(pattern)) {
throw new Error('Move parent-directory traversal into the `base` option');
} Type guard
const isSafeGlobPattern = (pattern: string): boolean =>
!pattern.replace(/^(\.\/|!)+/, '').startsWith('../'); Prevention
- Treat pattern as relative to base; put any `../` escape into base.
- Prefer a file-URL base (import.meta.url) for content in other packages.
When it happens
Trigger: `glob({ pattern: '../docs/**/*.md' })` — any pattern whose first characters are `../` (the check also strips leading `./` and `!` prefixes before testing).
Common situations: Content living outside src/content — e.g. a project-root docs/ folder, a monorepo shared content package, or generated content written to the repo root.
Related errors
- Glob patterns cannot start with `/`. Set the `base` option…
- Auto-generating collections for folders in "src/content/"…
- ** ** contains multiple entries with the same slug: ` `…
- •
- Content config not loaded
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/90688a4ebdc31a8e.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/content/loaders/glob.ts:88
}
function checkPrefix(pattern: string | Array<string>, prefix: string) {
if (Array.isArray(pattern)) {
return pattern.some((p) => p.startsWith(prefix));
}
return pattern.startsWith(prefix);
}
export const secretLegacyFlag = Symbol('astro.legacy-glob');
/**
* Loads multiple entries, using a glob pattern to match files.
* @param pattern A glob pattern to match files, relative to the content directory.
*/
export function glob(globOptions: GlobOptions & { [secretLegacyFlag]?: boolean }): Loader {
if (checkPrefix(globOptions.pattern, '../')) {
throw new Error(
'Glob patterns cannot start with `../`. Set the `base` option to a parent directory instead.',
);
}
if (checkPrefix(globOptions.pattern, '/')) {
throw new Error(
'Glob patterns cannot start with `/`. Set the `base` option to a parent directory or use a relative path instead.',
);
}
const isLegacy = !!globOptions[secretLegacyFlag];
const userGenerateId =
globOptions?.generateId ?? ((opts: GenerateIdOptions) => generateIdDefault(opts, isLegacy));
// Coerce to string so numeric ids from YAML don't cause Set strict-equality mismatches
// against string store keys in the untouched-entries cleanup. See #17624.
const generateId = (opts: GenerateIdOptions) => String(userGenerateId(opts));
const fileToIdMap = new Map<string, string>();
View on GitHub (pinned to e294953aa8)