{"record":{"id":"2602d40cd2c38264","repo":"flipped-aurora/gin-vue-admin","slug":"parent-plugin-is-not-a-directory","errorCode":null,"errorMessage":"parent plugin is not a directory","messagePattern":"parent plugin is not a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/auto_code_plugin.go","lineNumber":325,"sourceCode":"\t}\n\treturn archive, nil\n}\n\nfunc pluginInstallRoot(component, parentPlugin string) (string, error) {\n\troot, err := utils.JoinWithinRoot(global.GVA_CONFIG.AutoCode.Root, component, \"plugin\")\n\tif err != nil || parentPlugin == \"\" {\n\t\treturn root, err\n\t}\n\tparentPath, err := utils.JoinWithinRoot(root, parentPlugin)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tinfo, err := os.Stat(parentPath)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"parent plugin does not exist\")\n\t}\n\tif !info.IsDir() {\n\t\treturn \"\", errors.New(\"parent plugin is not a directory\")\n\t}\n\treturn utils.JoinWithinRoot(parentPath, \"subPlugin\")\n}\n\nfunc ensurePluginTargetAvailable(root, pluginName string) error {\n\ttarget, err := utils.JoinWithinRoot(root, pluginName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = os.Stat(target); err == nil {\n\t\treturn errors.New(\"plugin already exists\")\n\t} else if !os.IsNotExist(err) {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc copyPluginArchive(sourceRoot, destinationRoot string) error {","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/auto_code_plugin.go#L307-L343","documentation":"When installing a sub-plugin under a parent plugin, the installer resolves the parent's directory on disk and stats it. If the parent path exists but is a file (or symlink to a file) rather than a directory, it cannot host a subPlugin folder, so this error is thrown.","triggerScenarios":"Calling the sub-plugin install API with a parentPlugin name that collides with a regular file inside the component's plugin root (e.g. a stray `foo.zip` or a file named `foo` where the plugin directory should be).","commonSituations":"Leftover files accidentally named like a plugin; a plugin that was replaced by a tarball/zip left in place; path case-sensitivity mismatch causing Stat to hit a different file; corrupted checkout where the plugin directory was overwritten by a file.","solutions":["Inspect the parent plugin path on disk and remove or rename the stray file that occupies the plugin name.","Re-clone or restore the parent plugin directory from source so it exists as a proper directory.","Verify the parentPlugin name passed to the install API is spelled correctly (exact case).","Ensure the plugin root directory in AutoCode.Server config points to the intended component tree."],"exampleFix":"// before (on disk)\nserver/plugin/parent-plugin   <- plain file\n// after\nmkdir server/plugin/parent-plugin\n# restore plugin contents (plugin.go, etc.)\nserver/plugin/parent-plugin/plugin.go","handlingStrategy":"validation","validationCode":"const st = fs.statSync(path.join(serverPluginRoot, parentPlugin))\nif (!st.isDirectory()) throw new Error(`${parentPlugin} exists but is not a directory`)","typeGuard":null,"tryCatchPattern":"try {\n  await installSubPlugin(parent, child)\n} catch (e) {\n  if (e.message.includes('not a directory')) {\n    showHint('Parent plugin path is occupied by a file; remove or rename it.')\n  } else { throw e }\n}","preventionTips":["Never leave archives or temp files named like plugins inside the plugin root.","Periodically audit the plugin root for stray files.","Use consistent case when referencing plugin names to avoid case-sensitivity surprises."],"tags":["plugin-install","filesystem"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}