{"record":{"id":"9c9804eefd258dc6","repo":"github-linguist/linguist","slug":"commit-oid-must-be-a-commit-sha1","errorCode":null,"errorMessage":"commit_oid must be a commit SHA1","messagePattern":"commit_oid must be a commit SHA1","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/linguist/repository.rb","lineNumber":46,"sourceCode":"    # commit_oid - the sha1 of the commit that will be analyzed;\n    #              this is usually the master branch\n    # max_tree_size - the maximum tree size to consider for analysis (default: MAX_TREE_SIZE)\n    #\n    # Returns a Repository\n    def initialize(repo, commit_oid, max_tree_size = MAX_TREE_SIZE)\n      @repository = if repo.is_a? Linguist::Source::Repository\n        repo\n      else\n        # Allow this for backward-compatibility purposes\n        Linguist::Source::RuggedRepository.new(repo)\n      end\n      @commit_oid = commit_oid\n      @max_tree_size = max_tree_size\n\n      @old_commit_oid = nil\n      @old_stats = nil\n\n      raise TypeError, 'commit_oid must be a commit SHA1' unless commit_oid.is_a?(String)\n    end\n\n    # Public: Load the results of a previous analysis on this repository\n    # to speed up the new scan.\n    #\n    # The new analysis will be performed incrementally as to only take\n    # into account the file changes since the last time the repository\n    # was scanned\n    #\n    # old_commit_oid - the sha1 of the commit that was previously analyzed\n    # old_stats - the result of the previous analysis, obtained by calling\n    #             Repository#cache on the old repository\n    #\n    # Returns nothing\n    def load_existing_stats(old_commit_oid, old_stats)\n      @old_commit_oid = old_commit_oid\n      @old_stats = old_stats\n      nil","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/github-linguist/linguist/blob/b45dbe9b2825a43285bcd035861be91cc0a7299e/lib/linguist/repository.rb#L28-L64","documentation":"Linguist::Repository#initialize expects commit_oid to be a plain Ruby String holding the commit SHA1 to analyze, and raises TypeError when it is anything else. The guard is deliberately minimal — it only checks `commit_oid.is_a?(String)`; it does not validate 40-hex formatting, and it runs at the end of initialize after the instance variables are already assigned. The value is used later to diff trees and set git attributes, which is why non-Strings (Rugged::Commit, Rugged::Reference, nil) are rejected up front.","triggerScenarios":"1) `Linguist::Repository.new(repo, repo.last_commit)` — a Rugged::Commit object, not its oid. 2) Passing `repo.head.target` when it resolves to a Rugged::Reference instead of a String oid. 3) Passing nil or a GitRPC/response object from a hosting app. 4) `Repository.incremental(...)` forwarding a non-String commit_oid. Note the check happens last, so a failed construction still assigned @repository/@commit_oid — do not reuse the half-built object.","commonSituations":"Code written against Rugged assuming the library accepts commit objects; migrating older linguist call sites where an oid wrapper object was passed; passing the result of ref lookups (Reference/target) instead of resolved oid strings; passing branch name strings like 'main' works type-wise but is not a SHA1 despite the message wording.","solutions":["Resolve to a String oid first: `Linguist::Repository.new(repo, repo.rev_parse_oid('HEAD'))` or pass `commit.oid`.","If you have a Rugged::Reference, use `ref.target_id` (String) rather than `ref.target` (which may be an object).","Add a `unless commit_oid.is_a?(String)` guard at the call site with a clear message before constructing the Repository.","For branch names, resolve once (`repo.branches['main'].target_id`) and reuse the String."],"exampleFix":"# before\nstats = Linguist::Repository.new(rugged_repo, rugged_repo.last_commit).language_stats\n\n# after\noid = rugged_repo.rev_parse_oid('HEAD')\nstats = Linguist::Repository.new(rugged_repo, oid).language_stats","handlingStrategy":"type-guard","validationCode":"# Resolve to a String oid before constructing the Repository\noid =\n  case\n  when commit_oid.is_a?(String) then commit_oid\n  when commit_oid.respond_to?(:oid) then commit_oid.oid        # Rugged::Commit\n  when commit_oid.respond_to?(:target_id) then commit_oid.target_id # Rugged::Reference\n  else raise TypeError, 'commit_oid must be a commit SHA1'\n  end\n Linguist::Repository.new(rugged_repo, oid)","typeGuard":"def commit_sha1?(obj)\n  obj.is_a?(String) && obj.match?(/\\A[0-9a-f]{40}\\z/i)\nend","tryCatchPattern":"begin\n  Linguist::Repository.new(repo, oid)\nrescue TypeError => e\n  raise unless e.message == 'commit_oid must be a commit SHA1'\n  retry_with(repo.rev_parse_oid('HEAD'))\nend","preventionTips":["Always resolve refs with rev_parse_oid / branch.target_id so you pass Strings, never Rugged objects.","The guard only checks String-ness — also assert 40-hex format yourself to catch branch names and garbage early.","Note the check runs at the END of initialize; a raised TypeError still leaves instance vars set, so do not reuse the object."],"tags":["ruby","git","rugged","type-error","argument-validation"],"backgroundTag":"wrong-argument-type","analyzedSha":"b45dbe9b2825a43285bcd035861be91cc0a7299e","analyzedAt":"2026-08-21T15:30:05.145Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}