{"record":{"id":"2dacb86090453b90","repo":"xai-org/grok-build","slug":"unsupported-checkout-target-need-a-full-commit-oi","errorCode":null,"errorMessage":"unsupported checkout target (need a full commit oid or a simple git ref): {target}","messagePattern":"unsupported checkout target \\(need a full commit oid or a simple git ref\\): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/restore_fetch.rs","lineNumber":269,"sourceCode":"        .current_dir(repo)\n        .args([\"cat-file\", \"-t\", spec])\n        .stdout(Stdio::null())\n        .stderr(Stdio::null())\n        .output();\n    matches!(output, Ok(o) if o.status.success())\n}\n\n/// Fetch a checkout target (full oid or simple ref) if it is not already local.\n///\n/// # Errors\n///\n/// Unsafe/unsupported spec, spawn failure, timeout, or non-zero git exit.\npub(crate) fn fetch_checkout_target_if_missing(\n    repo: &Path,\n    target: &str,\n) -> Result<FetchCommitOutcome> {\n    let Some(spec) = origin_fetch_spec_for_checkout_target(target) else {\n        bail!(\"unsupported checkout target (need a full commit oid or a simple git ref): {target}\");\n    };\n    if is_full_object_id(spec) {\n        return fetch_commit_if_missing(repo, spec);\n    }\n    fetch_refspec_from_origin(repo, spec, RESTORE_FETCH_BUDGET)?;\n    Ok(FetchCommitOutcome::Fetched)\n}\n\nfn is_shallow_repository(repo: &Path) -> bool {\n    let output = git_command()\n        .current_dir(repo)\n        .args([\"rev-parse\", \"--is-shallow-repository\"])\n        .stdout(Stdio::piped())\n        .stderr(Stdio::null())\n        .output();\n    matches!(\n        output,\n        Ok(o) if o.status.success() && String::from_utf8_lossy(&o.stdout).trim() == \"true\"","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/restore_fetch.rs#L251-L287","documentation":"fetch_checkout_target_if_missing fetches a checkout target from origin only when it can map the target to a safe fetch spec via origin_fetch_spec_for_checkout_target. That mapping accepts full 40/64-hex object ids, simple safe git refs (branch names, refs/heads/…, refs/tags/…), and rewrites local remote-tracking names (origin/foo, refs/remotes/origin/foo) to the remote branch. This error is thrown for anything else: empty strings, targets starting with '-' (option-injection risk), refs containing ':', '*', '?', '[', backslash, whitespace, '..' or '@{', and abbreviated SHAs, which checkout locally but cannot be fetched from origin.","triggerScenarios":"Calling fetch_checkout_target_if_missing with an abbreviated SHA (e.g. 7-char commit prefix); a target with glob/range characters like 'main..other' or 'v1.*'; an empty or dash-prefixed target; a rev expression or log-pasted string (e.g. 'origin/HEAD -> origin/main') that origin_fetch_spec_for_checkout_target maps to None.","commonSituations":"User config or a snapshot manifest storing an abbreviated commit SHA instead of the full oid; a checkout target recorded as a revision range or wildcard refspec; targets pasted from git logs with arrow-style prefixes; corrupted restore metadata passing empty strings.","solutions":["Pass the full 40- or 64-character lowercase hex object id instead of an abbreviated SHA — resolve it with `git rev-parse <sha>` in a repo that has it, or fetch by branch/tag","Use a simple safe ref (branch name, refs/heads/…, refs/tags/…) as the checkout target instead of a range, wildcard, or rev expression","Fix the source of the bad target (snapshot manifest / user config) to store full oids or plain ref names; trim stray whitespace and arrow-style log output","If the commit only exists locally under an abbreviated form, expand it first (`git rev-parse --verify <abbrev>^{commit}`) before requesting a fetch"],"exampleFix":"// before: abbreviated SHA cannot be fetched from origin\nfetch_checkout_target_if_missing(repo, \"a1b2c3d\")?;\n// after: resolve to the full oid first\nlet full = git_rev_parse(repo, \"a1b2c3d\")?; // \"a1b2c3d…\" (40 hex chars)\nfetch_checkout_target_if_missing(repo, &full)?;","handlingStrategy":"validation","validationCode":"use crate::restore_fetch::{origin_fetch_spec_for_checkout_target, is_full_object_id};\nfn checkout_target_fetchable(target: &str) -> bool {\n    origin_fetch_spec_for_checkout_target(target).is_some()\n}\n// call before the API: assert!(checkout_target_fetchable(target), \"use a full oid or simple ref\");","typeGuard":"fn is_fetchable_checkout_target(target: &str) -> bool {\n    is_full_object_id(target)\n        || (!target.is_empty()\n            && !target.starts_with('-')\n            && !target.contains([':', '*', '?', '[', '\\\\', ' ', '\\t', '\\n', '\\r'])\n            && !target.contains(\"..\")\n            && !target.contains(\"{\"))\n}","tryCatchPattern":"match fetch_checkout_target_if_missing(repo, target) {\n    Ok(outcome) => outcome,\n    Err(e) if e.to_string().starts_with(\"unsupported checkout target\") => {\n        let full = expand_to_full_oid(repo, target) // git rev-parse --verify <target>^{commit}\n            .context(\"target must be a full commit oid or simple git ref\")?;\n        fetch_checkout_target_if_missing(repo, &full)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always store full 40/64-char commit oids in manifests and user config, never short SHAs","Reject ranges, wildcards, and rev expressions at config-parse time","Trim whitespace and strip log-style prefixes (e.g. 'origin/HEAD -> origin/main') from targets","Sanity-check targets with origin_fetch_spec_for_checkout_target (or the guard above) before calling restore APIs"],"tags":["git","fetch","validation","refspec","input-validation"],"backgroundTag":"invalid-git-refspec","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}