onetimesecret/onetimesecret · error · Onetime::ConfigError

#{name} is set to an unrecognized boolean (#{value.length} c

Error message

#{name} is set to an unrecognized boolean (#{value.length} chars, sha256:#{::Digest::SHA256.hexdigest(value)[0, BOOL_DIGEST_LENGTH]}). Use one of #{TRUTHY_VALUES.join('/')} or #{FALSEY_VALUES.join('/')}, or leave unset.

What it means

Onetime::Utils::Strings.strict_bool! resolves operator-supplied booleans strictly (ADR-033): after strip+downcase, blank means unset and returns the caller's default; otherwise the value must be exactly one of TRUTHY_VALUES (1/true/yes/on/y/t) or FALSEY_VALUES (0/false/no/off/n/f). Anything else raises Onetime::ConfigError rather than silently disabling a default-ON control. The message deliberately never echoes the value — it may be a misrouted credential — and instead reports its length plus an 8-hex-char SHA-256 tag you can recompute locally (lib/onetime/utils/strings.rb:299).

Source

Thrown at lib/onetime/utils/strings.rb:307

      # to confirm which value a log line refers to. Honest caveat: 8 hex
      # characters of a low-entropy value like "ture" is brute-forceable —
      # which is fine, because low-entropy values are exactly the case that
      # was never sensitive.
      #
      # @param name [String] Flag name, for the error message (e.g. 'RABBITMQ_VERIFY_PEER')
      # @param raw [Object] Raw value as supplied
      # @param default [Boolean] Value to use when raw is unset/blank
      # @return [Boolean]
      # @raise [Onetime::ConfigError] if raw is present but not a recognized token
      def strict_bool!(name, raw, default:)
        # Normalize exactly once so the blank guard and the token tables can
        # never disagree about what normalization means.
        value = raw.to_s.strip.downcase
        return default if value.empty?
        return true    if TRUTHY_VALUES.include?(value)
        return false   if FALSEY_VALUES.include?(value)

        raise Onetime::ConfigError,
          "#{name} is set to an unrecognized boolean " \
          "(#{value.length} chars, sha256:#{::Digest::SHA256.hexdigest(value)[0, BOOL_DIGEST_LENGTH]}). " \
          "Use one of #{TRUTHY_VALUES.join('/')} or #{FALSEY_VALUES.join('/')}, or leave unset."
      end
      module_function :strict_bool!

      private

      # Masks a single email address string
      # @param raw [String] Raw email address to mask
      # @return [String] Masked email address
      def mask_email_address(raw)
        addr = ::Mail::Address.new(raw)
        return mask_unparsed_address(raw) unless addr.local && addr.domain

        local  = mask_string_head(addr.local, EMAIL_MASK_MIN_LOCAL)
        domain = mask_domain(addr.domain)
        "#{local}@#{domain}"

View on GitHub (pinned to f81295e41b)

Solutions

  1. Set the flag to a recognized token — 1/true/yes/on/y/t or 0/false/no/off/n/f — or delete the line to take the default.
  2. To identify a mystery value, recompute the tag and compare with the sha256: prefix in the message: ruby -rdigest -e 'puts Digest::SHA256.hexdigest(ARGV[0].strip.downcase)[0,8]' 'suspected-value'.
  3. Remove literal quotes or stray characters introduced by systemd units or docker env files.
  4. If the reported length looks like a credential, grep your environment for a value misrouted into this flag's name.

Example fix

# before — typo meant to disable peer verification, fails fast at boot
RABBITMQ_VERIFY_PEER=ture
# Onetime::ConfigError: RABBITMQ_VERIFY_PEER is set to an unrecognized boolean (4 chars, sha256:...)

# after — recognized token (or leave unset for the default)
RABBITMQ_VERIFY_PEER=true
Defensive patterns

Strategy: type-guard

Validate before calling

# Lint boolean flags before boot
ok = Onetime::Utils::Strings::TRUTHY_VALUES + Onetime::Utils::Strings::FALSEY_VALUES
%w[RABBITMQ_VERIFY_PEER SMTP2GO_FASTACCEPT].each do |flag|
  v = ENV[flag].to_s.strip.downcase
  next if v.empty? || ok.include?(v)
  abort "#{flag} is not a recognized boolean (#{ok.join('/')}) — fix or unset"
end

Type guard

# Recognizer matching strict_bool!'s normalization exactly
module BoolGuard
  TOKENS = Onetime::Utils::Strings::TRUTHY_VALUES +
           Onetime::Utils::Strings::FALSEY_VALUES

  def self.strict_bool_token?(raw)
    TOKENS.include?(raw.to_s.strip.downcase)
  end
end

BoolGuard.strict_bool_token?(ENV['SMTP2GO_FASTACCEPT']) # false => strict_bool! would raise

Try / catch

begin
  fast = Onetime::Utils::Strings.strict_bool!('SMTP2GO_FASTACCEPT',
                                              ENV['SMTP2GO_FASTACCEPT'], default: false)
rescue Onetime::ConfigError => ex
  abort "Boot aborted — fix the environment first: #{ex.message}"
end

Prevention

When it happens

Trigger: Setting any flag consumed through strict_bool! — callers include enabled?, automatic_tax?, smtp2go_fastaccept/coerce_fastaccept, and QueueConfig flags such as RABBITMQ_VERIFY_PEER — to a token outside the tables: typos like 'ture' or 'flase', words like 'enabled'/'check', '1|0', or literal quotes left around the value.

Common situations: Hand-edited .env typo; systemd Environment= or docker --env-file quoting that leaves literal quotes in the value; a CI secret accidentally exported under a boolean flag's name (the long random string shows up as a length plus sha256 tag); 'TRUE' works (downcased) but 'yes please' does not.

Related errors


AI-assisted analysis of onetimesecret/onetimesecret@f81295e41b (2026-08-23). Data as JSON: /api/errors/3b66322a7b50a9c3. Report an issue: GitHub.