{"record":{"id":"ff0b6a15964065f8","repo":"louislam/dockge","slug":"fatal-error-this-config-datadir-is-not-a-direc","errorCode":null,"errorMessage":"Fatal error: ${this.config.dataDir} is not a directory","messagePattern":"Fatal error: (.+?) is not a directory","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"backend/dockge-server.ts","lineNumber":554,"sourceCode":"    checkTimezone(timezone : string) {\n        try {\n            dayjs.utc(\"2013-11-18 11:55\").tz(timezone).format();\n        } catch (e) {\n            throw new Error(\"Invalid timezone:\" + timezone);\n        }\n    }\n\n    /**\n     * Initialize the data directory\n     */\n    initDataDir() {\n        if (! fs.existsSync(this.config.dataDir)) {\n            fs.mkdirSync(this.config.dataDir, { recursive: true });\n        }\n\n        // Check if a directory\n        if (!fs.lstatSync(this.config.dataDir).isDirectory()) {\n            throw new Error(`Fatal error: ${this.config.dataDir} is not a directory`);\n        }\n\n        // Create data/stacks directory\n        if (!fs.existsSync(this.stacksDir)) {\n            fs.mkdirSync(this.stacksDir, { recursive: true });\n        }\n\n        log.info(\"server\", `Data Dir: ${this.config.dataDir}`);\n    }\n\n    /**\n     * Init or reset JWT secret\n     * @returns  JWT secret\n     */\n    async initJWTSecret() : Promise<Bean> {\n        let jwtSecretBean = await R.findOne(\"setting\", \" `key` = ? \", [\n            \"jwtSecret\",\n        ]);","sourceCodeStart":536,"sourceCodeEnd":572,"githubUrl":"https://github.com/louislam/dockge/blob/f809ae192b571944ad773e9866d3e67064ae8043/backend/dockge-server.ts#L536-L572","documentation":"initDataDir ensures the configured data directory exists, then lstats it and requires it to be a directory; otherwise it throws 'Fatal error: <dataDir> is not a directory'. This guards against the dataDir path existing but being a regular file, symlink to a file, socket, etc., which would break all subsequent file writes (db-config.json, stacks dir, sqlite db).","triggerScenarios":"--data-dir / DOCKGE_DATA_DIR (or config stack dir) points at a regular file; a file was accidentally created at the path (e.g. docker run mounted a single file where a dir was expected); a broken/symlink loop causing lstat to throw.","commonSituations":"Docker: '-v ./data.txt:/app/data' mounting a file as the data dir; typo in path where an existing filename matches; restore from backup extracted a file at the dataDir path.","solutions":["Remove or rename the file at the configured dataDir path and create a directory there: mkdir -p /app/data","Fix the --data-dir flag / DOCKGE_DATA_DIR env to point at a directory","In Docker, mount a host directory (or named volume), not a single file: -v ./dockge-data:/app/data"],"exampleFix":"// before\ndocker run -v ./dockge.db:/app/data dockge/dockge\n// after\ndocker run -v ./dockge-data:/app/data dockge/dockge","handlingStrategy":"validation","validationCode":"const fs = require(\"fs\");\nconst st = fs.existsSync(dataDir) ? fs.lstatSync(dataDir) : null;\nif (st && !st.isDirectory()) { throw new Error(`${dataDir} exists but is not a directory; move it and create a directory`); }\nif (!st) fs.mkdirSync(dataDir, { recursive: true });","typeGuard":"function isDirectoryPath(p) { try { return fs.lstatSync(p).isDirectory(); } catch { return false; } }","tryCatchPattern":"try { server.initDataDir(); } catch (e) { if (String(e.message).includes(\"is not a directory\")) { console.error(\"Fix --data-dir / DOCKGE_DATA_DIR:\", e.message); process.exit(1); } throw e; }","preventionTips":["In Docker, mount directories/named volumes, never single files, at the data dir path","Verify the dataDir config with `ls -la` before starting","Do not extract backups directly over the data dir path","Use explicit DOCKGE_DATA_DIR rather than relying on defaults"],"tags":["filesystem","config","startup","docker"],"backgroundTag":"invalid-data-directory","analyzedSha":"f809ae192b571944ad773e9866d3e67064ae8043","analyzedAt":"2026-08-31T19:13:50.919Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}