{"record":{"id":"d69941d976fefad8","repo":"ruby/ruby","slug":"failed-to-sign-gem-out","errorCode":null,"errorMessage":"Failed to sign gem:\n\n#{out}","messagePattern":"Failed to sign gem:\n\n#(.+?)","errorType":"exception","errorClass":"Gem::Exception","httpStatus":null,"severity":"warning","filePath":"lib/rubygems/commands/push_command.rb","lineNumber":166,"sourceCode":"  def attest!(name)\n    require \"open3\"\n    require \"shellwords\"\n    require \"tempfile\"\n\n    tempfile = Tempfile.new([File.basename(name, \".*\"), \".sigstore.json\"])\n    bundle = tempfile.path\n    tempfile.close(false)\n\n    env = defined?(Bundler.unbundled_env) ? Bundler.unbundled_env : ENV.to_h\n    # Gem.ruby is quoted if it contains whitespace, so split it into argv\n    # elements to keep the quotes out of the spawned command.\n    out, st = Open3.capture2e(\n      env,\n      *Shellwords.split(Gem.ruby), \"-S\", \"gem\", \"exec\", \"--conservative\",\n      \"sigstore-cli\", \"sign\", name, \"--bundle\", bundle,\n      unsetenv_others: true\n    )\n    raise Gem::Exception, \"Failed to sign gem:\\n\\n#{out}\" unless st.success?\n\n    bundle\n  end\n\n  def get_hosts_for(name)\n    gem_metadata = Gem::Package.new(name).spec.metadata\n\n    [\n      gem_metadata[\"default_gem_server\"],\n      gem_metadata[\"allowed_push_host\"],\n    ]\n  end\n\n  def get_push_scope\n    :push_rubygem\n  end\n\n  def attestation_supported_host?","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/lib/rubygems/commands/push_command.rb#L148-L184","documentation":"Raised by `gem push` while producing a Sigstore attestation: RubyGems shells out to `sigstore-cli sign <gem> --bundle <tmpfile>` (via `gem exec --conservative`) and raises this Gem::Exception, embedding the subprocess's combined stdout/stderr, when the child exits non-zero. In the current flow send_push_request_with_attestation rescues StandardError, prints 'Failed to push with attestation, retrying without attestation' with this message, and pushes the gem unattested — so the push usually completes, minus the attestation.","triggerScenarios":"Running `gem push` in an environment where auto-attestation triggers (non-JRuby, host is rubygems.org, ENV[\"GITHUB_ACTIONS\"] set) and the sigstore-cli subprocess fails: sigstore-cli not installed, no OIDC/federated credential available, or network failure reaching Fulcio/Rekor.","commonSituations":"GitHub Actions workflows missing `permissions: id-token: write`; self-hosted or containerized runners without the sigstore-cli gem; firewalled runners blocking sigstore.dev endpoints; a Bundler-sanitized environment confusing the spawned `gem exec` child.","solutions":["Read the subprocess output embedded in the message — it carries the actual sigstore-cli error (auth, network, or version problem).","In GitHub Actions, grant OIDC: add `permissions: { id-token: write, contents: read }` to the job or workflow.","Ensure sigstore-cli is usable in the pushing environment: `gem install sigstore-cli`, then verify with `gem exec --conservative sigstore-cli --version`.","Allow egress to fulcio.sigstore.dev / rekor.sigstore.dev (and the runner's OIDC provider) through proxies and firewalls.","If you do not need attestations, treat the warning as informational — the push completes without attestation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"require \"open3\"\nrequire \"shellwords\"\n\nout, st = Open3.capture2e(*Shellwords.split(Gem.ruby), \"-S\", \"gem\", \"exec\", \"--conservative\", \"sigstore-cli\", \"--version\")\nwarn \"sigstore-cli unusable, attestation will fail:\\n#{out}\" unless st.success?","typeGuard":null,"tryCatchPattern":"begin\n  Gem::Commands::PushCommand.new.invoke(*args)\nrescue Gem::Exception => e\n  raise unless e.message.start_with?(\"Failed to sign gem\")\n  warn \"pushing without attestation: #{e.message}\"\nend","preventionTips":["Grant `permissions: id-token: write` to GitHub Actions jobs that run gem push.","Keep sigstore-cli installed in any environment that pushes gems.","Whitelist egress to fulcio.sigstore.dev and rekor.sigstore.dev on firewalled runners.","Dry-run a dummy gem push in CI before release day to verify the signing path."],"tags":["rubygems","gem-push","sigstore","attestation","code-signing","github-actions"],"backgroundTag":"code-signing-failed","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}