{"record":{"id":"ae822df85247fb4d","repo":"basecamp/kamal","slug":"invalid-hooks-output-level-context-must-be","errorCode":null,"errorMessage":"Invalid hooks_output '#{level}'#{context}, must be one of: #{HOOKS_OUTPUT_LEVELS.join(', ')}","messagePattern":"Invalid hooks_output '#(.+?)'#(.+?), must be one of: #(.+?)","errorType":"validation","errorClass":"Kamal::ConfigurationError","httpStatus":null,"severity":"error","filePath":"lib/kamal/configuration.rb","lineNumber":447,"sourceCode":"    end\n\n    def role_names\n      raw_config.servers.is_a?(Array) ? [ \"web\" ] : raw_config.servers.keys.sort\n    end\n\n    def ensure_valid_hooks_output!\n      case raw_config.hooks_output\n      when Symbol, String\n        validate_hooks_output_level!(raw_config.hooks_output.to_sym)\n      when Hash\n        raw_config.hooks_output.each { |hook, level| validate_hooks_output_level!(level.to_sym, hook) }\n      end\n    end\n\n    def validate_hooks_output_level!(level, hook = nil)\n      return if HOOKS_OUTPUT_LEVELS.include?(level)\n      context = hook ? \" for hook '#{hook}'\" : \"\"\n      raise Kamal::ConfigurationError, \"Invalid hooks_output '#{level}'#{context}, must be one of: #{HOOKS_OUTPUT_LEVELS.join(', ')}\"\n    end\n\n    def git_version\n      @git_version ||=\n        if Kamal::Git.used?\n          if Kamal::Git.uncommitted_changes.present? && !builder.git_clone?\n            uncommitted_suffix = \"_uncommitted_#{SecureRandom.hex(8)}\"\n          end\n          [ Kamal::Git.revision, uncommitted_suffix ].compact.join\n        else\n          raise \"Can't use commit hash as version, no git repository found in #{Dir.pwd}\"\n        end\n    end\nend\n","sourceCodeStart":429,"sourceCodeEnd":462,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/configuration.rb#L429-L462","documentation":"Kamal::Configuration#ensure_valid_hooks_output! validates the `hooks_output:` setting — either a single Symbol/String level, or a Hash mapping individual hooks to levels — against HOOKS_OUTPUT_LEVELS, which is exactly [:quiet, :verbose]. Any other value (e.g. `detailed`, `debug`, `info`) raises with the invalid level, the hook context when the failure is hash-keyed, and the allowed list.","triggerScenarios":"Setting `hooks_output: detailed` (or any word other than quiet/verbose) in config/deploy.yml; a Hash form like `hooks_output: { \"pre-deploy\": \"info\" }`; values sourced from ENV strings that were never whitelisted; configs written against docs from a different kamal version whose level names changed.","commonSituations":"Guessing verbosity level names instead of checking constants; copying CI log-level conventions (info/debug) into kamal config; per-hook overrides added without validating each value.","solutions":["Use one of the two valid levels: `hooks_output: quiet` or `hooks_output: verbose`.","For per-hook control, use the hash form with valid values: `hooks_output: { pre-deploy: verbose }`.","If the value comes from ENV, restrict it in the template: `<%= %w[quiet verbose].include?(ENV[\"HOOKS_OUTPUT\"]) ? ENV[\"HOOKS_OUTPUT\"] : \"quiet\" %>`."],"exampleFix":"# config/deploy.yml — before\nhooks_output: detailed\n\n# after\nhooks_output: verbose","handlingStrategy":"validation","validationCode":"LEVELS = %i[quiet verbose].freeze\n\ndef valid_hooks_output?(value)\n  case value\n  when String, Symbol then LEVELS.include?(value.to_sym)\n  when Hash then value.values.all? { |v| LEVELS.include?(v.to_sym) }\n  else true # nil is fine (default)\n  end\nend","typeGuard":"def kamal_hooks_output?(value)\n  return true if value.nil?\n  case value\n  when String, Symbol then %w[quiet verbose].include?(value.to_s)\n  when Hash then value.values.all? { |v| %w[quiet verbose].include?(v.to_s) }\n  else false\n  end\nend","tryCatchPattern":"begin\n  config = Kamal::Configuration.new(create_config_files: false)\nrescue Kamal::ConfigurationError => e\n  puts \"Deploy config invalid: #{e.message}\"\n  exit 1\nend","preventionTips":["Restrict ENV-driven values to a whitelist in the ERB template.","Check Kamal::Configuration::HOOKS_OUTPUT_LEVELS after upgrading kamal.","Use the Hash form only when individual hooks need different levels."],"tags":["kamal","configuration","hooks","logging","validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}