jackwener/OpenCLI · error · ManifestImportError
failed to scan ${filePath}: ${getErrorMessage(cause)}
Error message
failed to scan ${filePath}: ${getErrorMessage(cause)} What it means
loadManifestEntries reads a CLI adapter module from clis/ to extract command definitions for the build manifest. If the file cannot be read from disk, it throws a ManifestImportError naming the file and the underlying cause, aborting that scan with a clear message instead of a raw fs error.
Source
Thrown at src/build-manifest.ts:156
* Throws `ManifestImportError` when a file looks like a CLI module but its
* import or post-import processing fails — callers must decide whether to
* surface or aggregate the failure.
*
* The third argument `clisDir` is used to compute the POSIX-style
* `sourceFile` relative path; it defaults to the package's `clis/` dir so
* existing test callers stay backward-compatible.
*/
export async function loadManifestEntries(
filePath: string,
site: string,
importer: (moduleHref: string) => Promise<unknown> = moduleHref => import(moduleHref),
clisDir: string = CLIS_DIR,
): Promise<ManifestEntry[]> {
let src: string;
try {
src = fs.readFileSync(filePath, 'utf-8');
} catch (err) {
throw new ManifestImportError(filePath, err);
}
// Helper / test modules that do not call cli() are not commands.
if (!CLI_MODULE_PATTERN.test(src)) return [];
try {
const modulePath = toModulePath(filePath, site);
const registry = getRegistry();
const before = new Map(registry.entries());
const mod = await importer(pathToFileURL(filePath).href);
const exportedCommands = Object.values(isRecord(mod) ? mod : {})
.filter(value => isCliCommandValue(value, site));
const runtimeCommands = exportedCommands.length > 0
? exportedCommands
: [...registry.entries()]
.filter(([key, cmd]) => {View on GitHub (pinned to 49907e53dc)
Solutions
- Confirm the file at filePath exists and is readable (ls -l, check permissions).
- Run the manifest build from the repository root so the default CLIS_DIR resolves correctly.
- Restore the deleted/renamed module or update the scanner's directory configuration.
- If on Linux/macOS, check filename casing matches the import/discovery path exactly.
Example fix
// before
buildManifest({ clisDir: path.join(process.cwd(), 'Cli') });
// after
buildManifest({ clisDir: path.join(process.cwd(), 'clis') }); Defensive patterns
Strategy: try-catch
Validate before calling
import { accessSync, constants } from 'fs';
function canRead(p) { try { accessSync(p, constants.R_OK); return true; } catch { return false; } } Type guard
function isManifestImportError(e: unknown): e is ManifestImportError {
return e instanceof Error && 'filePath' in e;
} Try / catch
try {
const entries = await loadManifestEntries(filePath);
} catch (err) {
if (err instanceof ManifestImportError) {
console.error(`skipping unreadable module ${err.filePath}:`, err.cause);
return [];
}
throw err;
} Prevention
- Run manifest builds from the repository root so CLIS_DIR resolves.
- Don't delete or rename clis/ modules while a build is running.
- Keep filename casing consistent across platforms.
- Check file permissions in CI (run as a user that can read the repo).
When it happens
Trigger: fs.readFileSync fails on the module path — file deleted between discovery and read, wrong clisDir passed, case-mismatched filename on case-sensitive filesystems, or permission denied.
Common situations: Renaming/deleting a clis/*.ts file while a build runs; running the manifest build from a different working directory so the relative clis dir doesn't resolve; symlinked repos where CLIS_DIR points elsewhere.
Related errors
- Verify command returned no metric for baseline
- File not found: ${path}
- File must be a readable text file: ${path}
- File could not be read: ${path}
- state.vscdb not found: ${db}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/91912c51cb41cd38.
Report an issue: GitHub.