{"record":{"id":"32a1f08392960e29","repo":"ytti/oxidized","slug":"hook-password-is-required","errorCode":null,"errorMessage":"hook.password is required","messagePattern":"hook\\.password is required","errorType":"validation","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"lib/oxidized/hook/xmppdiff.rb","lineNumber":45,"sourceCode":"        logger.info \"Joined #{cfg.channel}\"\n      end\n    rescue Timeout::Error\n      logger.info \"timed out\"\n      @client = nil\n      @muc = nil\n    end\n\n    @client.on_exception do\n      logger.info \"XMPP connection aborted, reconnecting\"\n      @client = nil\n      @muc = nil\n      connect\n    end\n  end\n\n  def validate_cfg!\n    raise KeyError, 'hook.jid is required' unless cfg.has_key?('jid')\n    raise KeyError, 'hook.password is required' unless cfg.has_key?('password')\n    raise KeyError, 'hook.channel is required' unless cfg.has_key?('channel')\n    raise KeyError, 'hook.nick is required' unless cfg.has_key?('nick')\n  end\n\n  def run_hook(ctx)\n    return unless ctx.node\n    return unless ctx.event.to_s == \"post_store\"\n\n    begin\n      Timeout.timeout(15) do\n        gitoutput = ctx.node.output.new\n        diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil\n\n        interesting = diff[:patch].lines.to_a[4..-1].any? do |line|\n          [\"+\", \"-\"].include?(line[0]) && (not [\"#\", \"!\"].include?(line[1]))\n        end\n\n        if interesting","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/ytti/oxidized/blob/687ed4262d9d21e54662e872e2383386d8498f7b/lib/oxidized/hook/xmppdiff.rb#L27-L63","documentation":"The xmppdiff hook validates at startup that jid, password, channel and nick are all present; a missing password raises KeyError 'hook.password is required' (lib/oxidized/hook/xmppdiff.rb:45). The password authenticates the JID against the XMPP server via @client.auth(cfg.password) in connect; without it the hook cannot log in to post diffs to the MUC.","triggerScenarios":"A hooks block with type: xmppdiff that sets jid (and maybe channel/nick) but omits password:, or a password key removed when the config was moved to a secrets pipeline and never templated back.","commonSituations":"Copy-pasting the example and filling in only the JID; scrubbing secrets from the config for commit but forgetting to re-inject them on the host.","solutions":["Add password: <xmpp account password> under the xmppdiff hook block (inject from your secrets manager, do not commit it)","Confirm all four keys jid, password, channel, nick are present at the same indentation","Restart oxidized and check the log for 'Authenticating to XMPP' to confirm the credentials work"],"exampleFix":"# before\nhooks:\n  xmpp:\n    type: xmppdiff\n    jid: oxidized@xmpp.example.com\n    channel: netdev@conference.example.com\n    nick: oxidized\n\n# after\nhooks:\n  xmpp:\n    type: xmppdiff\n    jid: oxidized@xmpp.example.com\n    password: secret\n    channel: netdev@conference.example.com\n    nick: oxidized","handlingStrategy":"validation","validationCode":"# check the xmppdiff block before oxidized loads it\nhook_cfg = YAML.load_file('/etc/oxidized/config')['hooks']['xmpp']\nmissing = %w[jid password channel nick].reject { |k| hook_cfg.key?(k) }\nraise ArgumentError, \"xmppdiff config missing: #{missing.join(', ')}\" unless missing.empty?","typeGuard":"def xmppdiff_cfg_valid?(cfg)\n  cfg.is_a?(Hash) && %w[jid password channel nick].all? { |k| cfg[k].is_a?(String) && !cfg[k].empty? }\nend","tryCatchPattern":"begin\n  hook.validate_cfg!\nrescue KeyError => e\n  abort \"fix the hooks config: #{e.message}\"\nend","preventionTips":["Inject the XMPP password from a secrets manager so the key is never dropped in transit","Key-check all four xmppdiff keys in the config pipeline","Use a dedicated account so credential rotation does not break humans"],"tags":["oxidized","xmppdiff-hook","xmpp","credentials","config-validation","keyerror"],"backgroundTag":"missing-config-key","analyzedSha":"687ed4262d9d21e54662e872e2383386d8498f7b","analyzedAt":"2026-08-23T11:19:42.084Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}