{"record":{"id":"05b60edaac2d7a1b","repo":"ytti/oxidized","slug":"hook-nick-is-required","errorCode":null,"errorMessage":"hook.nick is required","messagePattern":"hook\\.nick is required","errorType":"validation","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"lib/oxidized/hook/xmppdiff.rb","lineNumber":47,"sourceCode":"    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\n          connect if @muc.nil?\n","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/ytti/oxidized/blob/687ed4262d9d21e54662e872e2383386d8498f7b/lib/oxidized/hook/xmppdiff.rb#L29-L65","documentation":"The xmppdiff hook validates at startup that jid, password, channel and nick are all present; a missing nick raises KeyError 'hook.nick is required' (lib/oxidized/hook/xmppdiff.rb:47). The nick is the nickname used inside the MUC room; connect joins as cfg.channel + '/' + cfg.nick, so the room occupant shows up as <room>/<nick> and diffs are posted under that nick.","triggerScenarios":"A hooks block with type: xmppdiff that omits nick: (jid/password/channel set), or a nick key lost to a typo, rename, or indentation error.","commonSituations":"Copying a partial example; assuming the resource part of the JID is enough; editing the hooks block and breaking indentation.","solutions":["Add nick: oxidized under the xmppdiff hook block","Keep all four keys jid, password, channel, nick at the same indentation level","Restart oxidized and confirm the MUC join succeeds in the log"],"exampleFix":"# before\nhooks:\n  xmpp:\n    type: xmppdiff\n    jid: oxidized@xmpp.example.com\n    password: secret\n    channel: netdev@conference.example.com\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":["Pick a nick that identifies the bot so humans can filter it in the room","Key-check all four xmppdiff keys in the config pipeline","Avoid nicks with spaces or JID-forbidden characters"],"tags":["oxidized","xmppdiff-hook","xmpp","muc","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"}