dianping/cat · error · Error

button with key {key} must be an object

Error message

button with key {key} must be an object

What it means

When normalizing options.buttons, bootbox accepts either a function (short form for {callback: fn}) or an object per button key. If a button entry is neither — a string, number, array, null — it throws this Error naming the offending button key. Labels must be set via button.label, not by assigning a string to the button key itself.

Source

Thrown at cat-home/src/main/webapp/assets/js/uncompressed/bootbox.js:172

    options.backdrop = options.backdrop ? "static" : false;

    buttons = options.buttons;

    total = getKeyLength(buttons);

    each(buttons, function(key, button, index) {

      if ($.isFunction(button)) {
        // short form, assume value is our callback. Since button
        // isn't an object it isn't a reference either so re-assign it
        button = buttons[key] = {
          callback: button
        };
      }

      // before any further checks make sure by now button is the correct type
      if ($.type(button) !== "object") {
        throw new Error("button with key " + key + " must be an object");
      }

      if (!button.label) {
        // the lack of an explicit label means we'll assume the key is good enough
        button.label = key;
      }

      if (!button.className) {
        if (total <= 2 && index === total-1) {
          // always add a primary to the main option in a two-button dialog
          button.className = "btn-primary";
        } else {
          button.className = "btn-default";
        }
      }
    });

    return options;

View on GitHub (pinned to e815e74d4c)

Solutions

  1. Use the object form for custom buttons: { ok: { label: 'Yes', className: 'btn-primary', callback: fn } }.
  2. Use the function short form only when you need just a callback: { ok: fn }.
  3. When generating buttons programmatically, validate each entry: typeof b === 'function' || (b && typeof b === 'object').

Example fix

// before
bootbox.dialog({ message: 'Save?', buttons: { ok: 'Yes', cancel: 'No' } });

// after
bootbox.dialog({
  message: 'Save?',
  buttons: {
    ok:     { label: 'Yes', className: 'btn-primary', callback: save },
    cancel: { label: 'No' }
  }
});
Defensive patterns

Strategy: validation

Validate before calling

Object.keys(buttons).forEach(function (key) {
  var b = buttons[key];
  if (typeof b !== 'function' && !(b && typeof b === 'object')) {
    buttons[key] = { label: String(b) }; // coerce stray strings into labels
  }
});

Type guard

function isValidButtonEntry(b) {
  return typeof b === 'function' || (b !== null && typeof b === 'object' && !Array.isArray(b));
}

Prevention

When it happens

Trigger: buttons: { ok: 'Yes' } (string label instead of {label: 'Yes'}); buttons: { save: 1 }; buttons: { cancel: ['no'] }; a null entry from a partially populated config object.

Common situations: Assuming the buttons map takes label strings directly (common mistake from other dialog APIs); generating the buttons object from data where one record is malformed; mixing short-form functions with object entries and getting the shape wrong for one.

Related errors


AI-assisted analysis of dianping/cat@e815e74d4c (2026-08-14). Data as JSON: /api/errors/7cf5ace3a22638bb. Report an issue: GitHub.