{"record":{"id":"31632cda06f7361e","repo":"unslothai/unsloth","slug":"spec-name-source-revision-must-be-a-lowercase-fu","errorCode":null,"errorMessage":"{spec.name} source revision must be a lowercase full Git commit","messagePattern":"(.+?) source revision must be a lowercase full Git commit","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"studio/backend/utils/third_party_source.py","lineNumber":777,"sourceCode":"            if hashlib.sha256(destination_file.read_bytes()).hexdigest() != expected_digest:\n                raise RuntimeError(f\"{spec.name} source changed while preparing its runtime\")\n        for relative, content in _generated_file_contents(spec).items():\n            destination_file = staging / relative\n            destination_file.parent.mkdir(parents = True, exist_ok = True)\n            destination_file.write_bytes(content)\n        if not _valid_runtime(staging, spec, checkout):\n            raise RuntimeError(f\"The prepared {spec.name} runtime failed integrity validation\")\n        _replace_owned_directory(staging, runtime)\n    finally:\n        _remove_owned_path(workspace)\n\n\ndef ensure_pinned_source(\n    spec: PinnedSource, *, legacy_sources: tuple[Path | str, ...] = ()\n) -> Path:\n    revision = spec.revision.lower()\n    if _REVISION_PATTERN.fullmatch(revision) is None or revision != spec.revision:\n        raise RuntimeError(f\"{spec.name} source revision must be a lowercase full Git commit\")\n    for digest in (spec.source_tree_digest, spec.runtime_tree_digest):\n        if digest is not None and _SHA256_PATTERN.fullmatch(digest) is None:\n            raise RuntimeError(f\"{spec.name} source digest must be a lowercase SHA-256\")\n    if (spec.source_tree_digest is None) != (spec.runtime_tree_digest is None):\n        raise RuntimeError(f\"{spec.name} source and runtime digests must be configured together\")\n\n    parent = cache_root() / \"third-party-sources\" / spec.name\n    version_root = parent / revision\n    checkout = version_root / \"source\"\n    runtime = version_root / \"runtime-v1\"\n    if _valid_runtime(runtime, spec):\n        return runtime.resolve()\n\n    version_root.mkdir(parents = True, exist_ok = True)\n    try:\n        with FileLock(str(parent / \".install.lock\"), timeout = 300):\n            if _valid_runtime(runtime, spec):\n                return runtime.resolve()","sourceCodeStart":759,"sourceCodeEnd":795,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/third_party_source.py#L759-L795","documentation":"First check in ensure_pinned_source: spec.revision must be a full 40-char lowercase hex Git commit SHA — it must match _REVISION_PATTERN and equal its own .lower() (i.e. no uppercase). The library pins immutable commits, not mutable refs, and normalizes nothing for you: the check runs before any cache lookup or download.","triggerScenarios":"Passing revision as a tag/branch name ('v1.2.3', 'main'), a short SHA ('1a2b3c4'), an uppercase SHA ('1A2B...'), or a string with whitespace. The lowercase(pattern-match) + equality-with-original construction rejects any uppercase input explicitly.","commonSituations":"Copy-pasting a tag instead of the commit it resolves to; tooling that emits short SHAs; UIs uppercasing hex; config files generated from git describe output.","solutions":["Resolve the ref to a full commit: git rev-parse <tag>^{commit} and use that 40-char lowercase SHA as revision","Normalize before constructing the spec: revision=revision.strip().lower() and assert 40 hex chars","If you intended a floating ref, that is unsupported — pins must be immutable commits"],"exampleFix":"# before\nPinnedSource(name=\"foo\", package=\"foo\", revision=\"v1.2.3\", ...)\n\n# after\nsha = subprocess.check_output([\"git\", \"ls-remote\", url, \"refs/tags/v1.2.3\"]).split()[0].decode()\nPinnedSource(name=\"foo\", package=\"foo\", revision=sha.lower(), ...)","handlingStrategy":"validation","validationCode":"import re\n_REVISION_PATTERN = re.compile(r\"[0-9a-f]{40}\")\nassert _REVISION_PATTERN.fullmatch(spec.revision), (\n    f\"revision {spec.revision!r} must be a full 40-char lowercase hex commit SHA\"\n)","typeGuard":"import re\n\ndef is_full_commit_sha(revision: object) -> bool:\n    return isinstance(revision, str) and re.fullmatch(r\"[0-9a-f]{40}\", revision) is not None","tryCatchPattern":null,"preventionTips":["Always resolve tags/branches via git rev-parse <ref>^{commit} before writing them into a pin","Validate pins in config tests: revision matches ^[0-9a-f]{40}$ and both digests are paired 64-char lowercase hex"],"tags":["validation","configuration","git","pinned-source"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}