{"record":{"id":"3094e6fd2630276c","repo":"hexojs/hexo","slug":"magenta-tildify-publicdir-is-not-a-directory","errorCode":null,"errorMessage":"${magenta(tildify(publicDir))} is not a directory","messagePattern":"(.+?) is not a directory","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/plugins/console/generate.ts","lineNumber":162,"sourceCode":"  }\n  firstGenerate(): Promise<void> {\n    const { concurrency } = this;\n    const { route, log } = this.context;\n    const publicDir = this.context.public_dir;\n    const Cache = this.context.model('Cache');\n\n    // Show the loading time\n    const interval = prettyHrtime(process.hrtime(this.start));\n    log.info('Files loaded in %s', cyan(interval));\n\n    // Reset the timer for later usage\n    this.start = process.hrtime();\n\n\n    // Check the public folder\n    return stat(publicDir).then(stats => {\n      if (!stats.isDirectory()) {\n        throw new Error(`${magenta(tildify(publicDir))} is not a directory`);\n      }\n    }).catch(err => {\n      // Create public folder if not exists\n      if (err && err.code === 'ENOENT') {\n        return mkdirs(publicDir);\n      }\n\n      throw err;\n    }).then(() => {\n      const task = (fn, path) => () => fn.call(this, path);\n      const doTask = fn => fn();\n      const routeList = route.list();\n      const routeSet = new Set(routeList);\n      const publicFiles = Cache.filter(item => item._id.startsWith('public/')).map(item => item._id.substring(7));\n      const tasks = publicFiles.filter(path => !routeSet.has(path))\n        // Clean files\n        .map(path => task(this.deleteFile, path))\n        // Generate files","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/hexojs/hexo/blob/059cb17494d0632a053c24077f5bcb6ae92acc51/lib/plugins/console/generate.ts#L144-L180","documentation":"Thrown by the generate console (lib/plugins/console/generate.ts:162) during firstGenerate(). Hexo stats the configured public_dir; if the path exists but is NOT a directory (it is a regular file, symlink to a file, socket, etc.), it throws. An ENOENT (missing) is tolerated and the directory is created, but a file-in-place is not.","triggerScenarios":"config.public_dir resolves to a path occupied by a regular file (e.g. someone ran touch public, or a build artifact file sits there); a symlink pointing to a file; public_dir misconfigured to a filename. stat() succeeds, isDirectory() is false.","commonSituations":"A previous step wrote a file named 'public' instead of a folder; CI cache restoring a file at that path; public_dir set to something like public/index.html by mistake; a deploy tool creating a file placeholder.","solutions":["Remove or rename the offending file: rm <public_dir>, then let Hexo recreate the directory.","Check config.public_dir in _config.yml points to a folder name (default public), not a file path.","If it is a symlink, point it at a directory or remove it.","Clear CI caches that may have cached the path as a file."],"exampleFix":"# before\n# a file named 'public' exists in the project root\n\n# shell\nrm public\n# then re-run\nhexo generate","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nconst publicDir = hexo.public_dir;\ntry {\n  const stats = fs.statSync(publicDir);\n  if (!stats.isDirectory()) {\n    throw new Error(`${publicDir} is a file (${stats.isFile() ? 'file' : 'other'}), remove it before generate`);\n  }\n} catch (e) {\n  if (e.code !== 'ENOENT') throw e;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep public_dir as a folder name (default 'public').","Add a pre-generate check that removes a conflicting file at public_dir.","Audit CI caches so they do not restore a file at the public path."],"tags":["generate","filesystem","config","public-dir"],"backgroundTag":null,"analyzedSha":"059cb17494d0632a053c24077f5bcb6ae92acc51","analyzedAt":"2026-08-12T20:52:05.731Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}