puma/puma · error · ArgumentError

No such truststore file '#{truststore}'

Error message

No such truststore file '#{truststore}'

What it means

JRuby-only: MiniSSL::Context#truststore= (lib/puma/minissl.rb:248) validates the truststore path unless the value is exactly the symbol :default (which opts out and uses the JVM's default trust anchors). Any other value must point to an existing file or ArgumentError is raised.

Source

Thrown at lib/puma/minissl.rb:248

        attr_reader :keystore
        attr_reader :keystore_type
        attr_accessor :keystore_pass
        attr_reader :truststore
        attr_reader :truststore_type
        attr_accessor :truststore_pass
        attr_reader :cipher_suites
        attr_reader :protocols

        def keystore=(keystore)
          check_file keystore, 'Keystore'
          @keystore = keystore
        end

        def truststore=(truststore)
          # NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
          # compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
          unless truststore.eql?(:default)
            raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
          end
          @truststore = truststore
        end

        def keystore_type=(type)
          raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
          @keystore_type = type
        end

        def truststore_type=(type)
          raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
          @truststore_type = type
        end

        def cipher_suites=(list)
          list = list.split(',').map(&:strip) if list.is_a?(String)
          @cipher_suites = list
        end

View on GitHub (pinned to b8341dc946)

Solutions

  1. Create/copy the truststore (e.g. keytool -importcert) and point to its absolute path
  2. To rely on JVM defaults, pass `ssl_truststore: 'default'` through ssl_bind
  3. If configuring Context directly in Java/JRuby code, use the symbol :default, not the string

Example fix

# before
ssl_bind '0.0.0.0', '9292', {
  keystore: 'keystore.jks',
  'truststore' => '/etc/puma/truststore.jks'  # file not present
}

# after - use JVM default trust anchors
ssl_bind '0.0.0.0', '9292', {
  keystore: 'keystore.jks',
  'truststore' => 'default'
}
Defensive patterns

Strategy: validation

Validate before calling

ts = ENV['SSL_TRUSTSTORE']
ssl_bind host, port, {
  keystore: keystore,
  truststore: (ts == 'default' || File.exist?(ts) ? ts : abort("missing truststore #{ts}"))
}

Prevention

When it happens

Trigger: `ssl_bind` with a truststore option whose path does not exist on disk. Note the standard ContextBuilder converts the string 'default' to :default (lib/puma/minissl/context_builder.rb:27), so :default handling only applies through ssl_bind - assigning ctx.truststore = 'default' (String) directly checks for a literal file named 'default'.

Common situations: Porting an MRI puma config to JRuby where the truststore was never provisioned; wrong path after switching from system JAVA_HOME cacerts to a custom truststore; typos in the path.

Related errors


AI-assisted analysis of puma/puma@b8341dc946 (2026-08-21). Data as JSON: /api/errors/f560419af422d235. Report an issue: GitHub.