{"record":{"id":"ddecfa5b820e3a15","repo":"abhigyanpatwari/GitNexus","slug":"unable-to-read-eval-server-authentication-from-f","errorCode":null,"errorMessage":"Unable to read eval-server authentication from ${filePath}","messagePattern":"Unable to read eval-server authentication from (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/cli/eval-server.ts","lineNumber":107,"sourceCode":"): Promise<string | null> {\n  const directHost = validateHost(raw);\n  if (directHost && directHost !== 'localhost') return directHost;\n  if (directHost !== 'localhost' && !isHostname(raw)) return null;\n\n  try {\n    const address = await resolveHostname(raw);\n    return isIPv4(address) ? address : null;\n  } catch {\n    return null;\n  }\n}\n\nfunction readAuthTokenFile(filePath: string): string | undefined {\n  try {\n    return parseEnv(readFileSync(filePath, 'utf8')).GITNEXUS_AUTH_TOKEN?.trim() || undefined;\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n    throw new Error(`Unable to read eval-server authentication from ${filePath}`, { cause: error });\n  }\n}\n\n/** Resolve the bearer token from the shell, then .env.local, then .env. */\nexport function resolveEvalServerAuthToken(\n  env: NodeJS.ProcessEnv,\n  cwd: string = process.cwd(),\n): string | undefined {\n  if (Object.hasOwn(env, 'GITNEXUS_AUTH_TOKEN')) {\n    return env.GITNEXUS_AUTH_TOKEN?.trim() || undefined;\n  }\n\n  return (\n    readAuthTokenFile(path.join(cwd, '.env.local')) ?? readAuthTokenFile(path.join(cwd, '.env'))\n  );\n}\n\n/** True only for literal loopback addresses; DNS names are resolved before this check. */","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/cli/eval-server.ts#L89-L125","documentation":"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.","triggerScenarios":".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.","commonSituations":"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.","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"],"exampleFix":"# before\n$ ls -la .env.local  # -rw------- root root, server runs as node\n# after\n$ chown node:node .env.local && chmod 600 .env.local","handlingStrategy":"try-catch","validationCode":"import { accessSync, constants } from 'node:fs';\nfor (const p of ['.env.local', '.env']) {\n  try {\n    accessSync(p, constants.R_OK);\n  } catch {\n    if (require('node:fs').existsSync(p)) {\n      throw new Error(`${p} exists but is not readable — fix ownership/mode`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  resolveEvalServerAuthToken(process.env, cwd);\n} catch (err) {\n  const cause = (err as Error & { cause?: Error }).cause;\n  const code = (cause as NodeJS.ErrnoException | undefined)?.code;\n  if (code === 'EACCES') { /* chmod/chown the file named in the message */ }\n  else if (code === 'EISDIR') { /* .env.local is a directory — remove it */ }\n  else throw err;\n}","preventionTips":["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"],"tags":["env","auth","filesystem","permissions","eval-server"],"backgroundTag":"env-file-read-error","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}