{"record":{"id":"003e8979182af076","repo":"JuliusBrussee/caveman","slug":"envvar-does-not-point-to-an-executable-expli","errorCode":null,"errorMessage":"${envVar} does not point to an executable: ${explicit}","messagePattern":"(.+?) does not point to an executable: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/binary-installer/installer.mjs","lineNumber":158,"sourceCode":"  const reader = response.body.getReader();\n  try {\n    while (true) {\n      const { done, value } = await reader.read();\n      if (done) break;\n      hash.update(value);\n      await file.write(value);\n    }\n  } finally {\n    reader.releaseLock();\n    await file.close();\n  }\n  return hash.digest(\"hex\");\n}\n\nexport async function ensureBinary({ name, envVar }) {\n  const explicit = process.env[envVar];\n  if (explicit) {\n    if (!executable(explicit)) throw new Error(`${envVar} does not point to an executable: ${explicit}`);\n    return explicit;\n  }\n  const found = onPath(name);\n  if (found) return found;\n  const binDir = join(process.env.CAVEMAN_HOME ?? join(homedir(), \".caveman\"), \"bin\");\n  const target = join(binDir, binaryInstallFilename(name));\n  if (executable(target)) return target;\n\n  const { os, arch } = targetPlatform();\n  const artifact = `${name}_${os}_${arch}`;\n  const base = (process.env.CAVE_BINARY_RELEASE_BASE ?? BINARY_RELEASE_BASE_DEFAULT).replace(/\\/+$/, \"\");\n  const release = `${base}/${BINARY_RELEASE}`;\n  const timeout = timeoutMs();\n  const [checksumsResponse, signatureResponse] = await Promise.all([\n    asset(`${release}/checksums.txt`, timeout),\n    asset(`${release}/checksums.txt.keysig`, timeout),\n  ]);\n  const [checksums, signature] = await Promise.all([checksumsResponse.text(), signatureResponse.text()]);","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/shared/binary-installer/installer.mjs#L140-L176","documentation":"ensureBinary() first honors an explicit override: if the env var (CAVEMAN_MCP_BIN, CAVEMAN_SHRINK_BIN, or CAVEMAN_BROWSE_BIN depending on the tool) is set, its value must point at an executable file or setup fails immediately. This is a strict-contract check — an override that silently fell through to auto-download would hide operator intent.","triggerScenarios":"Setting the env var to a nonexistent path, a directory, a file without execute permission, a path with a typo, or a Windows path without .exe; also stale overrides pointing at deleted builds.","commonSituations":"Hardcoded absolute paths in Docker or compose files that break across image rebuilds; deploying to a new machine with copied env files; forgetting chmod +x on a manually placed binary; macOS quarantine dropping permissions after copy.","solutions":["Verify the printed path exists and is executable (ls -l, chmod +x if needed; use the .exe name on Windows)","Fix or remove the env var so it either points at a real executable or is unset, letting PATH lookup and auto-install take over","In containers, install or copy the binary in the same image stage that sets the env var"],"exampleFix":"# before\nexport CAVEMAN_MCP_BIN=/usr/local/bin/caveman-mcp   # not built there yet\n\n# after\nchmod +x /opt/caveman-mcp && export CAVEMAN_MCP_BIN=/opt/caveman-mcp\n# or simply: unset CAVEMAN_MCP_BIN","handlingStrategy":"validation","validationCode":"const fs = require(\"node:fs\");\nconst p = process.env.CAVEMAN_MCP_BIN; // or the SHRINK/BROWSE variant\nif (p) {\n  let ok = false;\n  try { fs.accessSync(p, fs.constants.X_OK); ok = true; } catch {}\n  if (!ok) throw new Error(`${p} is not executable — chmod +x or unset CAVEMAN_MCP_BIN`);\n}","typeGuard":"function isExecutablePath(p) {\n  try { fs.accessSync(p, fs.constants.X_OK); return true; } catch { return false; }\n}","tryCatchPattern":"try { bin = await ensureBinary({ name, envVar }); }\ncatch (e) {\n  if (/does not point to an executable/.test(String(e?.message)) && process.env[envVar]) {\n    delete process.env[envVar]; // drop the stale override, let auto-discovery run\n    return ensureBinary({ name, envVar });\n  }\n  throw e;\n}","preventionTips":["In Dockerfiles, COPY the binary and set its env var in the same stage after chmod +x","Prefer putting the binary on PATH over absolute-path env overrides that rot"],"tags":["configuration","env-var","installer","filesystem"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}