{"record":{"id":"f21025b3ff68c7f4","repo":"realm/jazzy","slug":"failed-full-command","errorCode":null,"errorMessage":"[!] Failed: #{full_command}","messagePattern":"\\[!\\] Failed: #(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/jazzy/executable.rb","lineNumber":41,"sourceCode":"      def execute_command(executable, args, raise_on_failure, env: {})\n        require 'shellwords'\n        bin = `which #{executable.to_s.shellescape}`.strip\n        raise \"Unable to locate the executable `#{executable}`\" if bin.empty?\n\n        require 'open4'\n\n        stdout = IO.new\n        stderr = IO.new($stderr)\n\n        options = { stdout: stdout, stderr: stderr, status: true }\n        status  = Open4.spawn(env, bin, *args, options)\n        unless status.success?\n          full_command = \"#{bin.shellescape} #{args.map(&:shellescape)}\"\n          output = stdout.to_s << stderr.to_s\n          if raise_on_failure\n            raise \"#{full_command}\\n\\n#{output}\"\n          else\n            warn(\"[!] Failed: #{full_command}\")\n          end\n        end\n        [stdout.to_s, status]\n      end\n    end\n  end\nend\n","sourceCodeStart":23,"sourceCodeEnd":49,"githubUrl":"https://github.com/realm/jazzy/blob/b3ee13dd057ebe9e1ce545063fc14d7e9de5176e/lib/jazzy/executable.rb#L23-L49","documentation":"jazzy's Jazzy::Executable.execute_command (lib/jazzy/executable.rb) runs external tools (sourcekitten, swift symbolgraph-extract, xcodebuild) via Open4.spawn and captures stdout/stderr. When the child process exits non-zero and the caller passed raise_on_failure = false, it does not raise: it prints \"[!] Failed: <command>\" to stderr via Kernel#warn and still returns [stdout, status] with the failing status. It is the non-fatal sibling of the `raise \"#{full_command}\\n\\n#{output}\"` on the adjacent line, used so a failed attempt can be reported while execution continues. Note a cosmetic quirk: `args.map(&:shellescape)` is an Array that is interpolated but never `join`ed, so the warned message shows the arguments as a Ruby array inspect string (e.g. [!] Failed: /path/to/tool [\"doc\", \"--module\", \"Foo\"]) rather than a copy-pasteable command line.","triggerScenarios":"Calling Jazzy::Executable.execute_command(executable, args, false, env: {...}) — the third positional argument false — when the spawned binary exits non-zero. In this jazzy snapshot all internal callers (sourcekitten.rb:229, symbol_graph.rb:28, symbol_graph.rb:103) pass true, so the warn branch fires for third-party code using Jazzy::Executable directly, or for older/newer jazzy versions that probe or fall back with raise_on_failure: false (e.g. trying xcodebuild vs swift build attempts). Typical failing children: `sourcekitten doc` when xcodebuild cannot build the scheme, `swift symbolgraph-extract` when the module name is wrong or the module cannot be compiled, or any env passed via the env: hash that breaks the build.","commonSituations":"Running jazzy (or code driving it) where the underlying Xcode toolchain step fails: missing/wrong --scheme or --xcodebuild-arguments so xcodebuild errors; --swift-build-tool symbolgraph without a valid --module; CODE_SIGNING/provisioning failures when building an app target for docs; xcode-select pointing at Command Line Tools instead of full Xcode; a Swift version that cannot compile the module. Also hit by gem users calling Jazzy::Executable.execute_command with false and not realizing the warn is the only signal — the returned stdout is then partial/empty output that later JSON parsing chokes on. Distinct from the earlier raise \"Unable to locate the executable\" at executable.rb:26, which fires when `which` cannot find the binary at all.","solutions":["Reconstruct and run the warned command manually to see the real error: the child's stderr is already teed to your terminal (stderr = IO.new($stderr)), and the message lists the binary plus the argument array — un-inspect the args and run them; the underlying xcodebuild/sourcekitten/swift failure output tells you what to fix","Fix the arguments or environment for the child tool: correct --scheme/--module/-target values, and for xcodebuild-backed runs add CODE_SIGNING_ALLOWED=NO (and friends) via build_tool_arguments or the env: hash when signing blocks the doc build","Verify the toolchain before invoking: `xcode-select -p` points at full Xcode (not CLT), `swift --version` matches the project, and the tool binary exists on PATH (otherwise you get the separate 'Unable to locate the executable' raise)","If you call execute_command yourself, stop passing false: pass true (as jazzy's own callers at sourcekitten.rb:229 and symbol_graph.rb:28 do) so a non-zero exit raises with the full command plus captured output instead of only warning","When you must keep raise_on_failure = false, check the second element of the returned pair ([stdout, status]) with status.success? / status.exitstatus and handle the failure explicitly instead of relying on the stderr warning"],"exampleFix":"# before\nstdout, _status = Jazzy::Executable.execute_command(\n  'xcodebuild', ['-scheme', 'MyApp', '-destination', 'generic/platform=iOS'], false)\nJSON.parse(stdout) # warns \"[!] Failed: ...\" then crashes on empty stdout\n\n# after\nstdout, status = Jazzy::Executable.execute_command(\n  'xcodebuild',\n  ['-scheme', 'MyApp', '-destination', 'generic/platform=iOS',\n   'CODE_SIGNING_ALLOWED=NO'],\n  true) # raise on failure: raises \"<cmd>\\n\\n<output>\" with the build log","handlingStrategy":"validation","validationCode":"require 'mkmf'\n\n# cheap preflight: tool must exist (avoids the sibling\n# 'Unable to locate the executable' raise)\nraise 'xcodebuild not found' unless find_executable0('xcodebuild')\n\nstdout, status = Jazzy::Executable.execute_command('xcodebuild', args, false)\nunless status.success?\n  STDERR.puts \"doc build failed (exit #{status.exitstatus}); skipping\"\n  return # never parse `stdout` after a non-zero exit\nend\nJSON.parse(stdout)","typeGuard":null,"tryCatchPattern":"# for the sibling raise_on_failure = true path (executable.rb:39),\n# the raise is a plain RuntimeError whose message is\n# \"<escaped command>\\n\\n<captured stdout+stderr>\":\nbegin\n  Jazzy::Executable.execute_command('swift',\n                                    ['symbolgraph-extract', *args], true)\nrescue RuntimeError => e\n  abort \"symbolgraph-extract failed:\\n#{e.message}\"\nend","preventionTips":["Treat \"[!] Failed:\" on stderr as a real failure, not noise: the command exited non-zero and the returned stdout must not be trusted for parsing","Always inspect the returned status (second element of [stdout, status]) when passing raise_on_failure = false; prefer passing true in CI so failures raise with the full output","Dry-run the exact child command (xcodebuild -scheme ..., swift symbolgraph-extract -module-name ...) in a terminal before wiring it into jazzy options","Pin a healthy toolchain: verify `xcode-select -p` points at full Xcode and the selected Swift version compiles the module you are documenting","For xcodebuild-backed doc builds of app targets, disable code signing via build_tool_arguments/env (e.g. CODE_SIGNING_ALLOWED=NO) so signing failures cannot fail the docs"],"tags":["jazzy","ruby","subprocess","xcodebuild","sourcekitten","stderr","exit-code"],"backgroundTag":"subprocess-exit-nonzero","analyzedSha":"b3ee13dd057ebe9e1ce545063fc14d7e9de5176e","analyzedAt":"2026-08-21T18:34:48.083Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}