{"record":{"id":"087a82e8a899cdba","repo":"santifer/career-ops","slug":"clone-of-url-sha-slice-0-10-failed-err","errorCode":null,"errorMessage":"clone of ${url}@${sha.slice(0, 10)} failed — ${err.stderr ? String(err.stderr).slice(0, 200) : err.message}","messagePattern":"clone of (.+?)@(.+?) failed — (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugin-install.mjs","lineNumber":54,"sourceCode":"  if (!m) throw new Error(`repo must be named \"career-ops-plugin-<name>\" (got \"${repoName}\")`);\n  return { url, id: m[1] };\n}\n\n/** Clone the EXACT pinned SHA into a fresh temp dir. Returns the temp dir path. */\nexport function safeClone(url, sha) {\n  if (!SHA_RE.test(sha || '')) throw new Error(`a 40-hex commit --sha is required (got ${JSON.stringify(sha)})`);\n  const dir = mkdtempSync(path.join(tmpdir(), 'co-plugin-'));\n  const git = (...args) => execFileSync('git', ['-c', 'protocol.ext.allow=never', '-c', 'protocol.file.allow=never', ...args], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 120_000 });\n  try {\n    git('-C', dir, 'init', '-q');\n    git('-C', dir, 'remote', 'add', 'origin', '--', url);\n    git('-C', dir, 'fetch', '--depth', '1', '--no-tags', '-q', 'origin', sha);\n    git('-C', dir, 'checkout', '-q', 'FETCH_HEAD');\n    rmSync(path.join(dir, '.git'), { recursive: true, force: true }); // drop VCS metadata (and any hooks)\n    return dir;\n  } catch (err) {\n    rmSync(dir, { recursive: true, force: true });\n    throw new Error(`clone of ${url}@${sha.slice(0, 10)} failed — ${err.stderr ? String(err.stderr).slice(0, 200) : err.message}`);\n  }\n}\n\n/** Check the minimum file set + a valid manifest whose id matches `expectId`. */\nexport function validateInstall(dir, expectId) {\n  const problems = [];\n  for (const f of MIN_FILES) if (!existsSync(path.join(dir, f))) problems.push(`missing required file: ${f}`);\n  if (problems.length) return { ok: false, problems, manifest: null };\n  let parsed;\n  try { parsed = JSON.parse(readFileSync(path.join(dir, 'manifest.json'), 'utf8')); }\n  catch (e) { return { ok: false, problems: [`manifest.json invalid JSON: ${e.message}`], manifest: null }; }\n  // validateManifest wants the dir to BE the plugin dir + the basename to equal id.\n  const tmpNamed = path.join(path.dirname(dir), expectId);\n  if (dir !== tmpNamed) { try { renameSync(dir, tmpNamed); dir = tmpNamed; } catch { /* validate in place using expectId */ } }\n  const manifest = validateManifest(parsed, dir, expectId);\n  if (!manifest) return { ok: false, problems: ['manifest failed validation (see ⚠️ above)'], manifest: null, dir };\n  const audit = auditPlugin(dir);\n  if (!audit.ok) return { ok: false, problems: audit.findings.map(f => `${f.file}: ${f.issue}`), manifest, dir };","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/plugin-install.mjs#L36-L72","documentation":"safeClone wraps every git invocation (init, remote add, fetch --depth 1 of the pinned sha, checkout) and, on failure, deletes the temp dir and re-throws a message containing the URL, the first 10 sha chars, and up to 200 chars of git's stderr (or the error message). It turns low-level execFileSync failures into one actionable, rate-limit-aware message.","triggerScenarios":"safeClone called when git is missing/not on PATH, the repo/SHA does not exist (fetch fails), the network is down or a proxy blocks github.com, GitHub is rate-limiting/unavailable, or the 120s timeout expires.","commonSituations":"Offline or corporate proxy blocks git; typo'd owner/repo after URL validation passed; pinned sha was garbage-collected or never fetched (force-pushed away); no SSH/network access from CI; git not installed in a slim container image.","solutions":["Read the embedded stderr segment — it names the concrete cause (auth, DNS, not-found, timeout)","Verify the URL and sha exist: `git ls-remote <url> <sha>`","Check network/proxy access to github.com and retry; on CI ensure git is installed","If the sha was force-pushed away, pick a new commit and re-pin"],"exampleFix":"// before\nconst dir = safeClone('https://github.com/acme/career-ops-plugin-demo', sha); // throws opaque exec errors\n// after\ntry {\n  const dir = safeClone(url, sha);\n} catch (e) {\n  console.error(e.message); // 'clone of https://...@a1b2c3d4e5 failed — fatal: could not read Username...'\n  // inspect stderr, check network, or verify the sha exists\n}","handlingStrategy":"retry","validationCode":"import { execFileSync } from 'child_process'; try { execFileSync('git', ['ls-remote', url, sha], { stdio: 'ignore' }); } catch { throw new Error(`unreachable repo or sha: ${url}@${sha}`); }","typeGuard":null,"tryCatchPattern":"let dir; for (let attempt = 1; attempt <= 3 && !dir; attempt++) { try { dir = safeClone(url, sha); } catch (e) { if (attempt === 3 || /not found|could not read/i.test(e.message)) throw e; await sleep(2 ** attempt * 500); } }","preventionTips":["Pre-verify reachability with git ls-remote before cloning","Install git in CI/container images","Retry with backoff on transient network errors; do not retry on not-found","Watch for GitHub rate limits; authenticate or slow down bulk installs"],"tags":["git","network","clone","subprocess"],"backgroundTag":"git-command-failed","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}