{"record":{"id":"e62d4673b405b81c","repo":"can1357/oh-my-pi","slug":"git-subdir-path-source-path-does-not-exist-in","errorCode":null,"errorMessage":"git-subdir path \"${source.path}\" does not exist in cloned repository","messagePattern":"git-subdir path \"(.+?)\" does not exist in cloned repository","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/source-resolver.ts","lineNumber":156,"sourceCode":"\t\tcase \"npm\":\n\t\t\tthrow new Error(\"npm plugin sources are not yet supported. Use git-based sources instead.\");\n\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown plugin source type: \"${(source as { source: string }).source}\"`);\n\t}\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\nasync function verifyDirExists(dirPath: string, errorMessage: string): Promise<void> {\n\ttry {\n\t\tconst stat = await fs.stat(dirPath);\n\t\tif (!stat.isDirectory()) {\n\t\t\tthrow new Error(errorMessage);\n\t\t}\n\t} catch (err) {\n\t\tif (isEnoent(err)) {\n\t\t\tthrow new Error(errorMessage);\n\t\t}\n\t\tthrow err;\n\t}\n}\n","sourceCodeStart":138,"sourceCodeEnd":161,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/source-resolver.ts#L138-L161","documentation":"Thrown by verifyDirExists (via resolveRelativeSource/resolveObjectSource) after a marketplace plugin source of type git-subdir has been cloned: the configured `source.path` subdirectory does not exist inside the cloned repository, or exists but is a file rather than a directory. The resolver validates the target immediately after cloning and cleans up the temp clone before failing, so a stale/partial clone is never left behind. It is a data/config validation error in the plugin source definition, not an infrastructure failure.","triggerScenarios":"Resolving a plugin source `{ source: \"git\", path: \"...\" }` where (1) the `path` is misspelled or uses the wrong case relative to the actual repo layout, (2) the subdirectory was renamed or deleted in the branch/commit being cloned, (3) `path` points at a file (e.g. a plugin.json) instead of a directory, or (4) the `sha`/branch pins an older commit where the directory did not yet exist.","commonSituations":"A marketplace.json author publishes a plugin entry pointing at `plugins/foo` but the repo layout is `plugin/foo`; a plugin moves directories in a refactor and pinned `sha` values in user configs still reference the old layout; users hand-edit a marketplace catalog path with a trailing filename; typos like `./src/plugin` vs `src/plugin`.","solutions":["Check the repository at the pinned branch/commit and correct `source.path` to the actual directory that exists (case-sensitive).","Point `path` at a directory, not a file — the plugin manifest file should be inside the directory.","If pinning a `sha`, update the pin to a commit where the subdirectory exists (e.g. the latest commit on the plugin's branch).","If you control the plugin repo, restore/recreate the directory at the referenced path or publish a corrected marketplace catalog entry."],"exampleFix":"// before\n{ \"source\": \"git\", \"repo\": \"acme/plugins\", \"path\": \"plugin/my-tool\" }\n// after (actual repo layout is plugins/my-tool)\n{ \"source\": \"git\", \"repo\": \"acme/plugins\", \"path\": \"plugins/my-tool\" }","handlingStrategy":"validation","validationCode":"import * as fs from \"node:fs/promises\";\nasync function gitSubdirSourceLooksValid(repoDir: string, sourcePath: string) {\n\ttry {\n\t\treturn (await fs.stat(path.join(repoDir, sourcePath))).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}","typeGuard":"function isNonEmptyRelativeDir(p: string): boolean {\n\treturn typeof p === \"string\" && p.length > 0 && !path.isAbsolute(p) && !p.endsWith(\"/\") && !p.includes(\"..\");\n}","tryCatchPattern":"try {\n\tconst resolved = await resolveObjectSource(source);\n} catch (err) {\n\tif (err instanceof Error && err.message.includes('does not exist in cloned repository')) {\n\t\t// surface source.path + repo so the user can fix the marketplace entry\n\t\tthrow new Error(`Plugin source path \"${source.path}\" not found in ${source.repo} at the pinned branch/commit`);\n\t}\n\tthrow err;\n}","preventionTips":["Pin sources to a branch/commit where you have verified the directory exists, and re-verify after upstream refactors.","Keep source.path a directory, never a file path.","Match path casing exactly to the repo layout (case-sensitive filesystems and git).","After renaming directories in a plugin repo, bump the pinned sha/branch in every marketplace catalog that references it."],"tags":["filesystem","configuration","path-validation","marketplace"],"backgroundTag":"subdirectory-not-found-in-repo","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}