junegunn/fzf · error
duplicate sort criteria: ${name}
Error message
duplicate sort criteria: ${name} What it means
parseTiebreak validates the --tiebreak option (comma-separated secondary sort criteria after score). Each criterion may appear at most once; the shared check() closure returns 'duplicate sort criteria: <name>' when the boolean flag for that criterion was already set by an earlier token.
Source
Thrown at src/options.go:1359
case "path":
return str, []criterion{byScore, byPathname, byLength}, nil
case "default":
return str, []criterion{byScore, byLength}, nil
}
return str, nil, errors.New("invalid scoring scheme: " + str + " (expected: default|path|history)")
}
func parseTiebreak(str string) ([]criterion, error) {
criteria := []criterion{byScore}
hasIndex := false
hasChunk := false
hasLength := false
hasBegin := false
hasEnd := false
hasPathname := false
check := func(notExpected *bool, name string) error {
if *notExpected {
return errors.New("duplicate sort criteria: " + name)
}
if hasIndex {
return errors.New("index should be the last criterion")
}
*notExpected = true
return nil
}
for _, str := range strings.Split(strings.ToLower(str), ",") {
switch str {
case "index":
if err := check(&hasIndex, "index"); err != nil {
return nil, err
}
case "chunk":
if err := check(&hasChunk, "chunk"); err != nil {
return nil, err
}
criteria = append(criteria, byChunk)View on GitHub (pinned to bd4efa277b)
Solutions
- List each criterion once: --tiebreak length,begin
- Deduplicate before passing: --tiebreak "$(echo "$TB" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd,)"
- Audit FZF_DEFAULT_OPTS and wrapper scripts for stacked --tiebreak flags
- Remember score is always first implicitly — never add it yourself
Example fix
# before export FZF_DEFAULT_OPTS='--tiebreak length --tiebreak length,end' # after export FZF_DEFAULT_OPTS='--tiebreak length,end'
Defensive patterns
Strategy: validation
Validate before calling
TB='length,begin'
IFS=',' read -ra C <<< "$(tr 'A-Z' 'a-z' <<< "$TB")"
DUP=$(printf '%s\n' "${C[@]}" | sort | uniq -d)
[ -z "$DUP" ] || { echo "duplicate tiebreaks: $DUP" >&2; exit 1; }
fzf --tiebreak "$TB" Prevention
- Deduplicate the criteria list before passing it
- Keep a single source of truth for --tiebreak; do not stack it in both FZF_DEFAULT_OPTS and the CLI
- Exclude 'score' — it is implicit and cannot be listed
When it happens
Trigger: --tiebreak length,length; --tiebreak begin,chunk,begin; --tiebreak index,length,index — any repeated token among index|chunk|length|begin|end|pathname.
Common situations: Appending to an existing FZF_DEFAULT_OPTS string that already contains --tiebreak (flags concatenate); copy-paste accumulation in shell rc files; generated tiebreak lists without dedup.
Related errors
- index should be the last criterion
- invalid sort criterion: ${str}
- at most 3 tiebreaks are allowed: ${str}
- invalid format: ${str}
- template should include at least 1 placeholder: ${str}
AI-assisted analysis of junegunn/fzf@bd4efa277b (2026-08-15).
Data as JSON: /api/errors/b6b5e9c5b7aaec41.
Report an issue: GitHub.