github-linguist/linguist · error · ArgumentError
Duplicate alias: #{name}
Error message
Duplicate alias: #{name} What it means
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.
Source
Thrown at lib/linguist/language.rb:69
if @name_index.key?(language.name)
raise ArgumentError, "Duplicate language name: #{language.name}"
end
# Language name index
@index[language.name.downcase] = @name_index[language.name.downcase] = language
# Index filesystem name if defined
if language.fs_name
if @name_index.key?(language.fs_name)
raise ArgumentError "Duplicate language name: #{language.fs_name}"
end
@index[language.fs_name.downcase] = @name_index[language.fs_name.downcase] = language
end
language.aliases.each do |name|
# All Language aliases should be unique. Raise if there is a duplicate.
if @alias_index.key?(name)
raise ArgumentError, "Duplicate alias: #{name}"
end
@index[name.downcase] = @alias_index[name.downcase] = language
end
language.extensions.each do |extension|
if extension !~ /^\./
raise ArgumentError, "Extension is missing a '.': #{extension.inspect}"
end
@extension_index[extension.downcase] << language
end
language.interpreters.each do |interpreter|
@interpreter_index[interpreter] << language
end
language.filenames.each do |filename|View on GitHub (pinned to b45dbe9b28)
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.
Example fix
# before (lib/linguist/languages.yml)
JavaScript:
aliases:
- js
- node
MyServerLang:
aliases:
- node
# after
MyServerLang:
aliases:
- mynode Defensive patterns
Strategy: validation
Validate before calling
new_aliases = %w[ts mylang] # what you plan to declare
# every language also registers a default alias: name.downcase with spaces -> dashes
taken = Linguist::Language.all.flat_map(&:aliases).map(&:downcase).to_set
conflicts = new_aliases.select { |a| taken.include?(a.downcase) }
raise ArgumentError, "aliases already in use: #{conflicts.join(', ')}" unless conflicts.empty? Try / catch
begin Linguist::Language.create(attributes) rescue ArgumentError => e raise if e.message !~ /Duplicate alias/ # strip the conflicting alias from attributes[:aliases] and retry once, or report end
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Duplicate language name: #{language.name}
- Duplicate language name: #{language.fs_name}
- Extension is missing a '.': #{extension.inspect}
- invalid type: #{@type}
- read_index is deprecated
AI-assisted analysis of github-linguist/linguist@b45dbe9b28 (2026-08-21).
Data as JSON: /api/errors/9ca42a0aba93796f.
Report an issue: GitHub.