{"record":{"id":"ca6d071dc7f952ff","repo":"grosser/parallel_tests","slug":"can-t-pass-failure-exit-code-and-highest-exit","errorCode":null,"errorMessage":"Can't pass --failure-exit-code and --highest-exit-status","messagePattern":"Can't pass --failure-exit-code and --highest-exit-status","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"lib/parallel_tests/cli.rb","lineNumber":384,"sourceCode":"\n      append_test_options(options, remaining)\n\n      options[:group_by] ||= :filesize if options[:only_group]\n\n      if options[:group_by] == :found && options[:single_process]\n        raise \"--group-by found and --single-process are not supported\"\n      end\n      allowed = [:filesize, :runtime, :found]\n      if !allowed.include?(options[:group_by]) && options[:only_group]\n        raise \"--group-by #{allowed.join(\" or \")} is required for --only-group\"\n      end\n\n      if options[:specify_groups] && options.keys.intersect?([:single_process, :isolate, :isolate_count])\n        raise \"Can't pass --specify-groups with any of these keys: --single, --isolate, or --isolate-n\"\n      end\n\n      if options[:failure_exit_code] && options[:highest_exit_status]\n        raise \"Can't pass --failure-exit-code and --highest-exit-status\"\n      end\n\n      options\n    end\n\n    def extract_file_paths(argv)\n      dash_index = argv.rindex(\"--\")\n      file_args_at = (dash_index || -1) + 1\n      [argv[file_args_at..], argv[0...(dash_index || 0)]]\n    end\n\n    def extract_test_options(argv)\n      dash_index = argv.index(\"--\") || -1\n      argv[dash_index + 1..]\n    end\n\n    def append_test_options(options, argv)\n      new_opts = extract_test_options(argv)","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/grosser/parallel_tests/blob/a06047856d3564ee77848e230d61cb6503037ad7/lib/parallel_tests/cli.rb#L366-L402","documentation":"Both flags define what exit status parallel_tests itself returns when a child test process fails: --failure-exit-code forces a constant you choose, while --highest-exit-status propagates the largest child exit code. They answer the same question differently, so parse_options! raises when both are present.","triggerScenarios":"`parallel_test --failure-exit-code 42 --highest-exit-status`, or a rake/CI string like PARALLEL_TEST_OPTS=\"--highest-exit-status --failure-exit-code 1\".","commonSituations":"CI pipelines that already use --highest-exit-status (to distinguish RSpec failure codes from crashes) gain --failure-exit-code when someone copies an RSpec invocation -- RSpec itself accepts a flag of the same name, and passing it after `--` (or via -o as a test option) is legal, which feeds the confusion.","solutions":["Pick one strategy: keep --failure-exit-code alone for a fixed CI-recognizable code, or --highest-exit-status alone to propagate child codes","If the intent was to configure RSpec's own exit code, pass it through to the runner instead: `parallel_test --highest-exit-status -o '--failure-exit-code 42'`","Prune the duplicated flag from rake tasks / CI env where it crept in"],"exampleFix":"# before\nparallel_test --highest-exit-status --failure-exit-code 42\n\n# after (fixed code)\nparallel_test --failure-exit-code 42\n# after (RSPEC's own flag passed through)\nparallel_test --highest-exit-status -o '--failure-exit-code 42'","handlingStrategy":"validation","validationCode":"opts = ENV.fetch('PARALLEL_TEST_OPTS', '').shellsplit\nif opts.include?('--failure-exit-code') && opts.include?('--highest-exit-status')\n  abort 'parallel_tests: choose either --failure-exit-code or --highest-exit-status, not both'\nend","typeGuard":"def exit_status_args_valid?(argv)\n  !(argv.include?('--failure-exit-code') && argv.include?('--highest-exit-status'))\nend","tryCatchPattern":"begin\n  ParallelTests::CLI.new.run(args)\nrescue RuntimeError => e\n  abort \"#{e.message} -- pass RSpec's own --failure-exit-code after -- or via -o if that was intended\"\nend","preventionTips":["Decide the CI exit-status contract once (fixed code vs propagated highest code) and encode it in one place","Remember RSpec accepts a same-named flag -- pass it through with -o '--failure-exit-code N' instead of at the parallel_tests level","Lint shared option strings for both flags in CI config review"],"tags":["ruby","parallel-tests","cli","exit-status","mutually-exclusive-options"],"backgroundTag":"mutually-exclusive-flags","analyzedSha":"a06047856d3564ee77848e230d61cb6503037ad7","analyzedAt":"2026-08-23T10:12:36.391Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}