ruvnet/RuView · error · Error
Refusing CLI access: repository does not match the configure
Error message
Refusing CLI access: repository does not match the configured trusted root
What it means
assertTrustedRuViewRepo (harness/ruview/src/repo-trust.js) is the harness's fail-closed trust gate. It realpath()s both repoRoot and trustedRoot (trustedRoot defaults to repoRoot) and requires the resolved repoRoot to be inside the trust anchor AND identical to it. Any subdirectory, parent directory, or symlink-resolution mismatch is refused before any host subprocess is spawned.
Source
Thrown at harness/ruview/src/repo-trust.js:14
// SPDX-License-Identifier: MIT
import { existsSync, realpathSync, readFileSync, statSync } from 'node:fs';
import { isAbsolute, join, relative } from 'node:path';
const REQUIRED_MARKERS = ['.git', 'README.md', 'v2'];
const RUVIEW_MARKERS = ['firmware', 'wifi_densepose'];
function isWithin(parent, child) {
const rel = relative(parent, child);
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
}
export function assertTrustedRuViewRepo(repoRoot, { trustedRoot = repoRoot } = {}) {
if (!repoRoot || !trustedRoot) throw new TypeError('repoRoot and trustedRoot are required');
const root = realpathSync(repoRoot);
const trustAnchor = realpathSync(trustedRoot);
if (!isWithin(trustAnchor, root) || root !== trustAnchor) throw new Error('Refusing CLI access: repository does not match the configured trusted root');
if (!statSync(root).isDirectory()) throw new Error('Refusing CLI access: trusted root is not a directory');
const missing = REQUIRED_MARKERS.filter((marker) => !existsSync(join(root, marker)));
if (missing.length || !RUVIEW_MARKERS.some((marker) => existsSync(join(root, marker)))) {
throw new Error(`Refusing CLI access: RuView repository markers are missing${missing.length ? ` (${missing.join(', ')})` : ''}`);
}
const readme = readFileSync(join(root, 'README.md'), 'utf8').slice(0, 131_072);
if (!/\b(?:RuView|wifi[- ]densepose)\b/i.test(readme)) throw new Error('Refusing CLI access: README does not identify a RuView checkout');
return root;
}
View on GitHub (pinned to 4685618388)
Solutions
- Pass the same absolute repository root for repoRoot and trustedRoot, or omit trustedRoot so it defaults to repoRoot.
- Resolve both through realpath before calling: const root = realpathSync(repoRoot); ...({ repoRoot: root, trustedRoot: root }).
- In the shell, confirm `realpath <repoRoot>` and `realpath <trustedRoot>` print identical paths, then use those literal paths.
Example fix
// before
runCodex({ prompt, repoRoot: '/link/ruview', trustedRoot: '/repos/RuView' }) // '/link/ruview' is a symlink
// after
import { realpathSync } from 'node:fs';
const root = realpathSync('/link/ruview');
runCodex({ prompt, repoRoot: root, trustedRoot: root }); Defensive patterns
Strategy: validation
Validate before calling
import { realpathSync } from 'node:fs';
const root = realpathSync(repoRoot);
const anchor = realpathSync(trustedRoot ?? repoRoot);
if (root !== anchor) {
throw new Error(`repoRoot ${root} does not equal trusted root ${anchor}`);
}
runCodex({ prompt, repoRoot: root, trustedRoot: anchor }); Type guard
import { realpathSync } from 'node:fs';
function isSameResolvedPath(a, b) {
return realpathSync(a) === realpathSync(b);
} Try / catch
try {
await runCodex({ prompt, repoRoot, trustedRoot });
} catch (e) {
if (e instanceof Error && e.message.includes('does not match the configured trusted root')) {
// fail closed: never widen the trust anchor automatically; surface to the operator
throw new Error(`trust gate refused ${repoRoot}; pass repoRoot === trustedRoot after realpath`);
}
throw e;
} Prevention
- Default trustedRoot to repoRoot (omit it) unless you have a reason to pin it.
- Resolve symlinks with realpathSync on both before comparing.
- Never auto-widen the trust anchor on failure — it is a security control; fix the paths instead.
When it happens
Trigger: runCodex({ repoRoot: '/repos/RuView/harness/ruview', trustedRoot: '/repos/RuView' }); a checkout reached through a symlink whose realpath differs from the configured trusted root; trustedRoot set to a home/workspace directory while repoRoot is the repo inside it.
Common situations: CI checkouts under symlinked workspace paths (/workspace -> /mnt/volumeN); monorepo tooling passing a subpackage as repoRoot; separate --repo and --trusted-root CLI flags drifting apart.
Related errors
- Refusing CLI access: RuView repository markers are missing${
- Refusing CLI access: README does not identify a RuView check
- guidance repoRoot must be a string or null
- Refusing CLI access: repository marker escapes the trusted r
- Refusing CLI access: README marker is not a regular file
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/8d1c726438aaa2ac.
Report an issue: GitHub.