{"record":{"id":"9ca42a0aba93796f","repo":"github-linguist/linguist","slug":"duplicate-alias-name","errorCode":null,"errorMessage":"Duplicate alias: #{name}","messagePattern":"Duplicate alias: #(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/linguist/language.rb","lineNumber":69,"sourceCode":"      if @name_index.key?(language.name)\n        raise ArgumentError, \"Duplicate language name: #{language.name}\"\n      end\n\n      # Language name index\n      @index[language.name.downcase] = @name_index[language.name.downcase] = language\n\n      # Index filesystem name if defined\n      if language.fs_name\n        if @name_index.key?(language.fs_name)\n          raise ArgumentError \"Duplicate language name: #{language.fs_name}\"\n        end\n        @index[language.fs_name.downcase] = @name_index[language.fs_name.downcase] = language\n      end\n\n      language.aliases.each do |name|\n        # All Language aliases should be unique. Raise if there is a duplicate.\n        if @alias_index.key?(name)\n          raise ArgumentError, \"Duplicate alias: #{name}\"\n        end\n\n        @index[name.downcase] = @alias_index[name.downcase] = language\n      end\n\n      language.extensions.each do |extension|\n        if extension !~ /^\\./\n          raise ArgumentError, \"Extension is missing a '.': #{extension.inspect}\"\n        end\n\n        @extension_index[extension.downcase] << language\n      end\n\n      language.interpreters.each do |interpreter|\n        @interpreter_index[interpreter] << language\n      end\n\n      language.filenames.each do |filename|","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/github-linguist/linguist/blob/b45dbe9b2825a43285bcd035861be91cc0a7299e/lib/linguist/language.rb#L51-L87","documentation":"Every language automatically gets a default alias derived from its name (name.downcase with spaces replaced by dashes, language.rb:445-447), and Language.create prepends it to the explicit `aliases:` list before registering each alias in @alias_index. If any alias — explicit or derived — is already claimed by another language, create raises ArgumentError. This is what usually surfaces an exact duplicate language name: two entries named 'Ruby' both try to register the default alias 'ruby', and because the name check compares raw casing against downcased keys, the collision is caught here first. The check makes Language.find_by_alias unambiguous.","triggerScenarios":"1) Adding `aliases: [ts]` to a language when TypeScript (or any language) already claims the alias 'ts'. 2) Adding a language whose name yields a default alias equal to an existing alias (e.g. naming a language \"Type Script\" produces default alias \"type-script\"; naming one \"Ruby\" twice produces colliding \"ruby\"). 3) Calling Language.create twice with the same name — the second call raises here. 4) An exact-case duplicate name in languages.yml, which reaches this line rather than the duplicate-name check.","commonSituations":"Contributing a language and adding a short alias (like 'clj', 'fs', 'ts') without checking the alias is free; renaming an existing language while keeping the old name as an alias of another entry; fork maintainers adding an alias that upstream later also adds, breaking the fork after a merge.","solutions":["Search languages.yml for the colliding alias string and pick a different alias for your entry.","If the collision comes from the implicit default alias, rename the language itself — the default alias is derived from the name.","If you intended two spellings to resolve to one language, keep the alias only on that language and remove it from the other.","When merging upstream changes into a fork, re-grep your custom aliases for collisions introduced by the merge."],"exampleFix":"# before (lib/linguist/languages.yml)\nJavaScript:\n  aliases:\n    - js\n    - node\nMyServerLang:\n  aliases:\n    - node\n\n# after\nMyServerLang:\n  aliases:\n    - mynode","handlingStrategy":"validation","validationCode":"new_aliases = %w[ts mylang] # what you plan to declare\n# every language also registers a default alias: name.downcase with spaces -> dashes\ntaken = Linguist::Language.all.flat_map(&:aliases).map(&:downcase).to_set\nconflicts = new_aliases.select { |a| taken.include?(a.downcase) }\nraise ArgumentError, \"aliases already in use: #{conflicts.join(', ')}\" unless conflicts.empty?","typeGuard":null,"tryCatchPattern":"begin\n  Linguist::Language.create(attributes)\nrescue ArgumentError => e\n  raise if e.message !~ /Duplicate alias/\n  # strip the conflicting alias from attributes[:aliases] and retry once, or report\nend","preventionTips":["Check an alias against Language.find_by_alias(name) before declaring it — nil means it is free.","Remember the implicit default alias derived from the language name counts toward collisions.","Keep aliases minimal: only add ones users actually type in .gitattributes 'linguist-language=' directives."],"tags":["ruby","yaml","configuration","boot","linguist","alias-conflict"],"backgroundTag":"duplicate-config-entries","analyzedSha":"b45dbe9b2825a43285bcd035861be91cc0a7299e","analyzedAt":"2026-08-21T15:30:05.145Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}