From 724867d48db9005cc66a4b32273abb8b45c5b45a Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 12 May 2026 23:44:11 -0500 Subject: [PATCH] fix(installer): descriptive error when module definition missing after clone (#2377) * fix(installer): throw descriptive error when module definition missing after clone When a stable tag predates a module restructure (e.g. baut v1.14.0 had payload/source dirs, but the registry pointed to skills/module.yaml which only exists on main), findExternalModuleSource silently returned the configured but non-existent path. This caused a confusing ENOENT inside getFileList/copyModuleWithFiltering rather than a clear error. Now throws with the version that was cloned and a --next hint when the install channel was stable, so users know exactly how to recover. Closes #2372 * style: fix prettier formatting in external-manager.js * style: apply prettier formatting --- tools/installer/modules/external-manager.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/installer/modules/external-manager.js b/tools/installer/modules/external-manager.js index 7d2add4fb..d8ecf8a5d 100644 --- a/tools/installer/modules/external-manager.js +++ b/tools/installer/modules/external-manager.js @@ -524,8 +524,20 @@ class ExternalModuleManager { return path.dirname(rootCandidate); } - // Nothing found: return configured path (preserves old behavior for error messaging) - return path.dirname(configuredPath); + // Nothing found: the cloned ref does not contain a recognizable module structure. + // This happens when a stable tag predates a module restructure (e.g. the repo + // moved files from payload/ to skills/ after the tag was cut). Returning a + // non-existent path silently causes a confusing ENOENT deep inside copyModuleWithFiltering; + // throw a descriptive error here instead so the user knows what happened and how to recover. + const resolution = ExternalModuleManager._resolutions.get(moduleCode); + const versionHint = resolution?.version ? `version ${resolution.version}` : 'the cloned version'; + const channelHint = + resolution?.channel === 'stable' ? ` Try reinstalling with \`--next=${moduleCode}\` to use the latest main branch instead.` : ''; + throw new Error( + `Module '${moduleCode}' was downloaded but its module definition was not found. ` + + `Expected '${moduleDefinitionPath}' to exist in ${versionHint}, but it is missing. ` + + `The repository may have been restructured after this release was tagged.${channelHint}`, + ); } cachedModules = null; }