{"record":{"id":"4e28a0b8da3a5dd9","repo":"junegunn/fzf","slug":"index-should-be-the-last-criterion","errorCode":null,"errorMessage":"index should be the last criterion","messagePattern":"index should be the last criterion","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/options.go","lineNumber":1362,"sourceCode":"\t\treturn str, []criterion{byScore, byLength}, nil\n\t}\n\treturn str, nil, errors.New(\"invalid scoring scheme: \" + str + \" (expected: default|path|history)\")\n}\n\nfunc parseTiebreak(str string) ([]criterion, error) {\n\tcriteria := []criterion{byScore}\n\thasIndex := false\n\thasChunk := false\n\thasLength := false\n\thasBegin := false\n\thasEnd := false\n\thasPathname := false\n\tcheck := func(notExpected *bool, name string) error {\n\t\tif *notExpected {\n\t\t\treturn errors.New(\"duplicate sort criteria: \" + name)\n\t\t}\n\t\tif hasIndex {\n\t\t\treturn errors.New(\"index should be the last criterion\")\n\t\t}\n\t\t*notExpected = true\n\t\treturn nil\n\t}\n\tfor _, str := range strings.Split(strings.ToLower(str), \",\") {\n\t\tswitch str {\n\t\tcase \"index\":\n\t\t\tif err := check(&hasIndex, \"index\"); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase \"chunk\":\n\t\t\tif err := check(&hasChunk, \"chunk\"); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcriteria = append(criteria, byChunk)\n\t\tcase \"pathname\":\n\t\t\tif err := check(&hasPathname, \"pathname\"); err != nil {\n\t\t\t\treturn nil, err","sourceCodeStart":1344,"sourceCodeEnd":1380,"githubUrl":"https://github.com/junegunn/fzf/blob/bd4efa277b49ef34ca4025bff9b1a288e1980eff/src/options.go#L1344-L1380","documentation":"Within parseTiebreak's check() closure: 'index' means input order, which is only meaningful as the final tiebreaker. Once hasIndex is true, any further criterion token triggers this error, telling you index must come last in the --tiebreak list.","triggerScenarios":"--tiebreak index,length (index first, then more); --tiebreak begin,index,end (end appears after index). Both add criteria after index was consumed.","commonSituations":"Users treating the list as unordered; migrating from scripts that appended 'index' early; editing an existing comma list by inserting a criterion before the trailing index.","solutions":["Move index to the end: --tiebreak length,begin,index","Drop trailing criteria after index, or drop index if earlier ordering matters more"],"exampleFix":"# before\nfzf --tiebreak index,length\n# after\nfzf --tiebreak length,index","handlingStrategy":"validation","validationCode":"TB='length,index'\nIFS=',' read -ra C <<< \"$TB\"\nLAST=\"${C[-1]}\"\n[[ \"$LAST\" == index ]] || grep -qxF index <(printf '%s\\n' \"${C[@]}\") && { echo 'index must be last' >&2; exit 1; }\nfzf --tiebreak \"$TB\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Convention: order criteria by decreasing importance and always put index last","When editing a tiebreak list, insert new criteria before the trailing index","Remember the ordering is semantically meaningful — it is not a set"],"tags":["fzf","options-parsing","sorting","tiebreak","go"],"backgroundTag":null,"analyzedSha":"bd4efa277b49ef34ca4025bff9b1a288e1980eff","analyzedAt":"2026-08-15T06:11:46.983Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}