{"record":{"id":"bcaa21a59a29ed02","repo":"nanocoai/nanoclaw","slug":"plugin-rejected-rel-is-not-a-regular-file-or","errorCode":null,"errorMessage":"Plugin rejected: \"${rel}\" is not a regular file or directory","messagePattern":"Plugin rejected: \"(.+?)\" is not a regular file or directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/templates/plugin-dir.ts","lineNumber":74,"sourceCode":"      }\n      const rel = relDir ? `${relDir}/${name}` : name;\n      const abs = path.join(absDir, name);\n      if (entry.isSymbolicLink()) {\n        throw new Error(`Plugin rejected: \"${rel}\" is a symlink (symlinks are not allowed in plugins)`);\n      }\n      if (depth + 1 > MAX_PLUGIN_DEPTH) {\n        throw new Error(`Plugin rejected: \"${rel}\" exceeds the maximum nesting depth of ${MAX_PLUGIN_DEPTH}`);\n      }\n      entries += 1;\n      if (entries > MAX_PLUGIN_FILES) {\n        throw new Error(`Plugin rejected: more than ${MAX_PLUGIN_FILES} entries`);\n      }\n      if (entry.isDirectory()) {\n        visit(rel, depth + 1);\n        continue;\n      }\n      if (!entry.isFile()) {\n        throw new Error(`Plugin rejected: \"${rel}\" is not a regular file or directory`);\n      }\n      const stat = fs.lstatSync(abs);\n      files.push({ rel, abs, size: stat.size, mode: stat.mode });\n      totalBytes += stat.size;\n      if (totalBytes > MAX_PLUGIN_TOTAL_BYTES) {\n        throw new Error(`Plugin rejected: total size exceeds ${MAX_PLUGIN_TOTAL_BYTES} bytes`);\n      }\n    }\n  };\n\n  visit('', 0);\n  return files;\n}\n\n/**\n * Copy a validated plugin tree. `dest` is replaced wholesale so re-stamping\n * is idempotent. Re-walks `src` at copy time so the validation and the copy\n * see the same tree.","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/templates/plugin-dir.ts#L56-L92","documentation":"Thrown by the plugin directory walker when a directory entry is neither a directory nor a regular file. This happens for symlinks (entry.isFile() is false because isFile uses stat semantics only for regular files — here the code checks Dirent type), FIFOs, sockets, or device nodes. The library enforces that plugin payloads contain only regular files and directories.","triggerScenarios":"Calling walkPluginDir on a plugin folder that contains a Unix socket, FIFO, device file, or a symlink to a file (Dirent.isFile() returns false for symlinks). Any lstat-derived Dirent that is not DIRECTORY or FILE triggers it.","commonSituations":"A plugin dir containing a symlink (e.g. node_modules-style links or a shared config symlink), or a leftover socket file from a dev process inside the copied plugin folder.","solutions":["Remove or replace non-regular entries (symlinks, sockets, FIFOs) in the plugin directory with real files or copy their targets","If a symlink is needed, copy the target file's contents into the plugin dir instead","Check for stray files: find plugins/<name> -type s -o -type p -o -type l"],"exampleFix":"# before\nplugins/my-plugin/config.json -> ../../shared/config.json  (symlink)\n# after\ncp ../../shared/config.json plugins/my-plugin/config.json","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nconst path = require('path');\nfunction assertRegularFiles(dir) {\n  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {\n    const abs = path.join(dir, e.name);\n    if (e.isDirectory()) assertRegularFiles(abs);\n    else if (!e.isFile()) throw new Error(`non-regular entry: ${abs} (${e.isSymbolicLink() ? 'symlink' : 'other'})`);\n  }\n}\n// assertRegularFiles(pluginDir) before walkPluginDir","typeGuard":null,"tryCatchPattern":"try { walkPluginDir(dir); } catch (e) { if (/not a regular file or directory/.test(e.message)) { /* surface offending path from message, fix tree */ } throw e; }","preventionTips":["Never commit symlinks inside plugin directories","Add a lint step to your template build that rejects non-regular files","Clean dev artifacts (sockets, temp files) before packaging"],"tags":["filesystem","plugin","validation","symlink"],"backgroundTag":"invalid-file-type","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}