{"record":{"id":"90688a4ebdc31a8e","repo":"withastro/astro","slug":"glob-patterns-cannot-start-with-set-the-ba","errorCode":null,"errorMessage":"Glob patterns cannot start with `../`. Set the `base` option to a parent directory instead.","messagePattern":"Glob patterns cannot start with `\\.\\./`\\. Set the `base` option to a parent directory instead\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/content/loaders/glob.ts","lineNumber":88,"sourceCode":"}\n\nfunction checkPrefix(pattern: string | Array<string>, prefix: string) {\n\tif (Array.isArray(pattern)) {\n\t\treturn pattern.some((p) => p.startsWith(prefix));\n\t}\n\treturn pattern.startsWith(prefix);\n}\n\nexport const secretLegacyFlag = Symbol('astro.legacy-glob');\n\n/**\n * Loads multiple entries, using a glob pattern to match files.\n * @param pattern A glob pattern to match files, relative to the content directory.\n */\n\nexport function glob(globOptions: GlobOptions & { [secretLegacyFlag]?: boolean }): Loader {\n\tif (checkPrefix(globOptions.pattern, '../')) {\n\t\tthrow new Error(\n\t\t\t'Glob patterns cannot start with `../`. Set the `base` option to a parent directory instead.',\n\t\t);\n\t}\n\tif (checkPrefix(globOptions.pattern, '/')) {\n\t\tthrow new Error(\n\t\t\t'Glob patterns cannot start with `/`. Set the `base` option to a parent directory or use a relative path instead.',\n\t\t);\n\t}\n\n\tconst isLegacy = !!globOptions[secretLegacyFlag];\n\tconst userGenerateId =\n\t\tglobOptions?.generateId ?? ((opts: GenerateIdOptions) => generateIdDefault(opts, isLegacy));\n\t// Coerce to string so numeric ids from YAML don't cause Set strict-equality mismatches\n\t// against string store keys in the untouched-entries cleanup. See #17624.\n\tconst generateId = (opts: GenerateIdOptions) => String(userGenerateId(opts));\n\n\tconst fileToIdMap = new Map<string, string>();\n","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/content/loaders/glob.ts#L70-L106","documentation":"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.","triggerScenarios":"`glob({ pattern: '../docs/**/*.md' })` — any pattern whose first characters are `../` (the check also strips leading `./` and `!` prefixes before testing).","commonSituations":"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.","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."],"exampleFix":"// before\nconst Docs = defineCollection({\n  loader: glob({ pattern: '../docs/**/*.md' }),\n});\n\n// after\nconst Docs = defineCollection({\n  loader: glob({ pattern: '**/*.md', base: '../..' }), // src/content -> project root\n});","handlingStrategy":"validation","validationCode":"function normalizeGlobOptions(pattern: string, base?: string) {\n  if (pattern.replace(/^(\\.\\/|!)+/, '').startsWith('../')) {\n    // fold the escape into base instead\n    const segments = pattern.split('/');\n    let i = 0;\n    while (segments[i] === '..' || segments[i] === '.' || segments[i] === '!..') i += segments[i] === '..' ? 1 : 0, segments[i] === '..' ? i++ : i;\n    // simpler: hand-write the base for known cases\n  }\n  return { pattern, base };\n}\n\n// pragmatic check:\nif (/^(\\.\\/|!)*(\\.\\.\\/)/.test(pattern)) {\n  throw new Error('Move parent-directory traversal into the `base` option');\n}","typeGuard":"const isSafeGlobPattern = (pattern: string): boolean =>\n  !pattern.replace(/^(\\.\\/|!)+/, '').startsWith('../');","tryCatchPattern":null,"preventionTips":["Treat pattern as relative to base; put any `../` escape into base.","Prefer a file-URL base (import.meta.url) for content in other packages."],"tags":["content-collections","glob-loader","path","config"],"backgroundTag":"glob-pattern-misuse","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}