ytti/oxidized · error · KeyError

hook.channel is required

Error message

hook.channel is required

What it means

The xmppdiff hook validates at startup that jid, password, channel and nick are all present; a missing channel raises KeyError 'hook.channel is required' (lib/oxidized/hook/xmppdiff.rb:46). The channel is the MUC room the hook joins; connect builds the MUC JID as cfg.channel + '/' + cfg.nick via Jabber::MUC::SimpleMUCClient#join, so it must be the full room JID, e.g. netdev@conference.example.com.

Source

Thrown at lib/oxidized/hook/xmppdiff.rb:46

      end
    rescue Timeout::Error
      logger.info "timed out"
      @client = nil
      @muc = nil
    end

    @client.on_exception do
      logger.info "XMPP connection aborted, reconnecting"
      @client = nil
      @muc = nil
      connect
    end
  end

  def validate_cfg!
    raise KeyError, 'hook.jid is required' unless cfg.has_key?('jid')
    raise KeyError, 'hook.password is required' unless cfg.has_key?('password')
    raise KeyError, 'hook.channel is required' unless cfg.has_key?('channel')
    raise KeyError, 'hook.nick is required' unless cfg.has_key?('nick')
  end

  def run_hook(ctx)
    return unless ctx.node
    return unless ctx.event.to_s == "post_store"

    begin
      Timeout.timeout(15) do
        gitoutput = ctx.node.output.new
        diff = gitoutput.get_diff ctx.node, ctx.node.group, ctx.commitref, nil

        interesting = diff[:patch].lines.to_a[4..-1].any? do |line|
          ["+", "-"].include?(line[0]) && (not ["#", "!"].include?(line[1]))
        end

        if interesting
          connect if @muc.nil?

View on GitHub (pinned to 687ed4262d)

Solutions

  1. Add channel: netdev@conference.example.com (full MUC room JID) under the xmppdiff hook block
  2. Keep jid, password, channel, nick at the same indentation level
  3. Restart oxidized and confirm the log shows 'Joined <channel>'

Example fix

# before
hooks:
  xmpp:
    type: xmppdiff
    jid: oxidized@xmpp.example.com
    password: secret
    nick: oxidized

# after
hooks:
  xmpp:
    type: xmppdiff
    jid: oxidized@xmpp.example.com
    password: secret
    channel: netdev@conference.example.com
    nick: oxidized
Defensive patterns

Strategy: validation

Validate before calling

# check the xmppdiff block before oxidized loads it
hook_cfg = YAML.load_file('/etc/oxidized/config')['hooks']['xmpp']
missing = %w[jid password channel nick].reject { |k| hook_cfg.key?(k) }
raise ArgumentError, "xmppdiff config missing: #{missing.join(', ')}" unless missing.empty?

Type guard

def xmppdiff_cfg_valid?(cfg)
  cfg.is_a?(Hash) && %w[jid password channel nick].all? { |k| cfg[k].is_a?(String) && !cfg[k].empty? }
end

Try / catch

begin
  hook.validate_cfg!
rescue KeyError => e
  abort "fix the hooks config: #{e.message}"
end

Prevention

When it happens

Trigger: A hooks block with type: xmppdiff that omits channel: (for example jid/password/nick were copied but the room was not), or a channel key lost to indentation or renamed to room.

Common situations: Copying a partial example; using a bare room name without the conference domain, which passes validation but fails to join at runtime; editing the hooks block and breaking indentation.

Related errors


AI-assisted analysis of ytti/oxidized@687ed4262d (2026-08-23). Data as JSON: /api/errors/7af953e8b73b1779. Report an issue: GitHub.