abhigyanpatwari/GitNexus · error · Error
Unable to read eval-server authentication from
Error message
Unable to read eval-server authentication from ${filePath} What it means
While resolving the eval-server bearer token, readAuthTokenFile reads .env.local / .env and lets only ENOENT pass silently (a missing file is fine). Any other failure — EACCES, EISDIR, or the env parser choking on the content — is rethrown wrapped, with the original error attached as `cause`. The message names the exact file path it could not read.
Solutions
- Check the path named in the message: ls -la <filePath> and confirm it is a readable regular file
- Fix ownership/permissions: chmod 644 (or chown to the running user) — only .env.local/.env, never secret-wide loosening
- Inspect (error as any).cause for the underlying errno to distinguish EACCES vs EISDIR vs parse failure
- If the file is genuinely unwanted, remove it — absence is handled gracefully
Example fix
# before $ ls -la .env.local # -rw------- root root, server runs as node # after $ chown node:node .env.local && chmod 600 .env.local
Defensive patterns
Strategy: try-catch
Validate before calling
import { accessSync, constants } from 'node:fs';
for (const p of ['.env.local', '.env']) {
try {
accessSync(p, constants.R_OK);
} catch {
if (require('node:fs').existsSync(p)) {
throw new Error(`${p} exists but is not readable — fix ownership/mode`);
}
}
} Try / catch
try {
resolveEvalServerAuthToken(process.env, cwd);
} catch (err) {
const cause = (err as Error & { cause?: Error }).cause;
const code = (cause as NodeJS.ErrnoException | undefined)?.code;
if (code === 'EACCES') { /* chmod/chown the file named in the message */ }
else if (code === 'EISDIR') { /* .env.local is a directory — remove it */ }
else throw err;
} Prevention
- Keep .env.local/.env owned by the uid that runs the server (chmod 600, right owner)
- Missing files are fine — only unreadable/parsable ones throw; don't preemptively create empty ones with bad modes
- In containers, COPY --chmod or chown env files to the runtime user
When it happens
Trigger: .env.local or .env with restrictive permissions (chmod 600 owned by another user), the path existing as a directory, or content the env parser rejects; resolveEvalServerAuthToken(env, cwd) then throws instead of falling through to the next source.
Common situations: Docker runs where the env file was COPY'd with root ownership but the server runs as a non-root uid; shared machines; CI copying env files with 600 from a different user; a mounted secret directory at the .env path.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Could not read
- GitNexus could not move the LadybugDB WAL sidecar at
- GitNexus: unable to verify main DB file before orphan…
- parsedfile-cache: could not reset durable chunk generation…
- -32001
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-08-20).
Data as JSON: /api/errors/ddecfa5b820e3a15.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/cli/eval-server.ts:107
): Promise<string | null> {
const directHost = validateHost(raw);
if (directHost && directHost !== 'localhost') return directHost;
if (directHost !== 'localhost' && !isHostname(raw)) return null;
try {
const address = await resolveHostname(raw);
return isIPv4(address) ? address : null;
} catch {
return null;
}
}
function readAuthTokenFile(filePath: string): string | undefined {
try {
return parseEnv(readFileSync(filePath, 'utf8')).GITNEXUS_AUTH_TOKEN?.trim() || undefined;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
throw new Error(`Unable to read eval-server authentication from ${filePath}`, { cause: error });
}
}
/** Resolve the bearer token from the shell, then .env.local, then .env. */
export function resolveEvalServerAuthToken(
env: NodeJS.ProcessEnv,
cwd: string = process.cwd(),
): string | undefined {
if (Object.hasOwn(env, 'GITNEXUS_AUTH_TOKEN')) {
return env.GITNEXUS_AUTH_TOKEN?.trim() || undefined;
}
return (
readAuthTokenFile(path.join(cwd, '.env.local')) ?? readAuthTokenFile(path.join(cwd, '.env'))
);
}
/** True only for literal loopback addresses; DNS names are resolved before this check. */View on GitHub (pinned to ac9a4e9abd)