{"record":{"id":"da49b2bd986a164c","repo":"supabase/supabase","slug":"snippets-management-folder-env-var-is-not-set-ple","errorCode":null,"errorMessage":"SNIPPETS_MANAGEMENT_FOLDER env var is not set. Please set it to use snippets properly.","messagePattern":"SNIPPETS_MANAGEMENT_FOLDER env var is not set\\. Please set it to use snippets properly\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/studio/lib/api/snippets.utils.ts","lineNumber":144,"sourceCode":"  // Remove path traversal sequences and normalize\n  const sanitized = path.basename(name)\n  if (sanitized !== name || name.includes('\\0')) {\n    throw new Error('Invalid name: path traversal or null bytes detected')\n  }\n  return sanitized\n}\n\n/**\n * Gets a snapshot of the filesystem structure including files and folders.\n * Pass `includeContent: false` for listings (sidebar/folder tree), which only need metadata —\n * this skips reading every `.sql` file off disk.\n * @returns An array of files and folders with their metadata\n */\nexport async function getFilesystemEntries({\n  includeContent = true,\n}: { includeContent?: boolean } = {}): Promise<FilesystemEntry[]> {\n  if (SNIPPETS_DIR === '') {\n    throw new Error(\n      'SNIPPETS_MANAGEMENT_FOLDER env var is not set. Please set it to use snippets properly.'\n    )\n  }\n\n  // Ensure the snippets directory exists\n  try {\n    await fs.access(SNIPPETS_DIR)\n  } catch {\n    await fs.mkdir(SNIPPETS_DIR, { recursive: true })\n  }\n\n  const entries: FilesystemEntry[] = []\n\n  const readEntriesRecursively = async (\n    dirPath: string,\n    folderName: string | null\n  ): Promise<void> => {\n    const items = await fs.readdir(dirPath, { withFileTypes: true })","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/supabase/supabase/blob/beee91b9c2228dd57302dec75c733baaa84ab543/apps/studio/lib/api/snippets.utils.ts#L126-L162","documentation":"getFilesystemEntries — the foundation of every snippet/folder operation — refuses to run when SNIPPETS_DIR (from SNIPPETS_MANAGEMENT_FOLDER env var) is the empty string. Self-hosted snippets are stored on disk; without a configured root directory the entire feature is non-functional, so it fails loud rather than silently writing to cwd.","triggerScenarios":"Any snippets API route (list, get, save, delete, folder ops) is hit on a self-hosted instance where SNIPPETS_MANAGEMENT_FOLDER env var is unset/empty. Every snippet function funnels through getFilesystemEntries, so all of them surface this.","commonSituations":"New self-hosted deployment missing the env var in docker-compose/.env; the var was renamed; the Studio container started before the env was injected.","solutions":["Set SNIPPETS_MANAGEMENT_FOLDER to an absolute, writable path in the Studio service env and restart.","Ensure the path is inside a persisted volume so snippets survive container restarts.","Verify the Studio process has read/write permissions on that directory."],"exampleFix":"# before\n# .env (missing)\n\n# after\n# .env\nSNIPPETS_MANAGEMENT_FOLDER=/var/lib/studio/snippets","handlingStrategy":"validation","validationCode":"import { SNIPPETS_DIR } from './snippets.constants'\nfunction assertSnippetsConfigured() {\n  if (!SNIPPETS_DIR) {\n    throw new Error('SNIPPETS_MANAGEMENT_FOLDER env var must be set')\n  }\n}","typeGuard":"const isSnippetsConfigured = (): boolean => Boolean(process.env.SNIPPETS_MANAGEMENT_FOLDER)","tryCatchPattern":"try {\n  await getFilesystemEntries()\n} catch (e) {\n  if (e instanceof Error && e.message.includes('SNIPPETS_MANAGEMENT_FOLDER')) {\n    // show setup banner instead of crashing the snippets UI\n    return []\n  }\n  throw e\n}","preventionTips":["Set SNIPPETS_MANAGEMENT_FOLDER in the base env template (docker-compose, .env.example).","Add a health check on boot that verifies the dir is writable.","Mount a persistent volume at that path so snippets survive restarts."],"tags":["configuration","env-vars","self-hosted","snippets","filesystem"],"backgroundTag":null,"analyzedSha":"beee91b9c2228dd57302dec75c733baaa84ab543","analyzedAt":"2026-08-12T06:51:48.935Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}