{"record":{"id":"7cf5ace3a22638bb","repo":"dianping/cat","slug":"button-with-key-key-must-be-an-object","errorCode":null,"errorMessage":"button with key {key} must be an object","messagePattern":"button with key (.+?) must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cat-home/src/main/webapp/assets/js/uncompressed/bootbox.js","lineNumber":172,"sourceCode":"    options.backdrop = options.backdrop ? \"static\" : false;\n\n    buttons = options.buttons;\n\n    total = getKeyLength(buttons);\n\n    each(buttons, function(key, button, index) {\n\n      if ($.isFunction(button)) {\n        // short form, assume value is our callback. Since button\n        // isn't an object it isn't a reference either so re-assign it\n        button = buttons[key] = {\n          callback: button\n        };\n      }\n\n      // before any further checks make sure by now button is the correct type\n      if ($.type(button) !== \"object\") {\n        throw new Error(\"button with key \" + key + \" must be an object\");\n      }\n\n      if (!button.label) {\n        // the lack of an explicit label means we'll assume the key is good enough\n        button.label = key;\n      }\n\n      if (!button.className) {\n        if (total <= 2 && index === total-1) {\n          // always add a primary to the main option in a two-button dialog\n          button.className = \"btn-primary\";\n        } else {\n          button.className = \"btn-default\";\n        }\n      }\n    });\n\n    return options;","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/dianping/cat/blob/e815e74d4c2dd74edac831241f1253fcc7d25381/cat-home/src/main/webapp/assets/js/uncompressed/bootbox.js#L154-L190","documentation":"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.","triggerScenarios":"buttons: { ok: 'Yes' } (string label instead of {label: 'Yes'}); buttons: { save: 1 }; buttons: { cancel: ['no'] }; a null entry from a partially populated config object.","commonSituations":"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.","solutions":["Use the object form for custom buttons: { ok: { label: 'Yes', className: 'btn-primary', callback: fn } }.","Use the function short form only when you need just a callback: { ok: fn }.","When generating buttons programmatically, validate each entry: typeof b === 'function' || (b && typeof b === 'object')."],"exampleFix":"// before\nbootbox.dialog({ message: 'Save?', buttons: { ok: 'Yes', cancel: 'No' } });\n\n// after\nbootbox.dialog({\n  message: 'Save?',\n  buttons: {\n    ok:     { label: 'Yes', className: 'btn-primary', callback: save },\n    cancel: { label: 'No' }\n  }\n});","handlingStrategy":"validation","validationCode":"Object.keys(buttons).forEach(function (key) {\n  var b = buttons[key];\n  if (typeof b !== 'function' && !(b && typeof b === 'object')) {\n    buttons[key] = { label: String(b) }; // coerce stray strings into labels\n  }\n});","typeGuard":"function isValidButtonEntry(b) {\n  return typeof b === 'function' || (b !== null && typeof b === 'object' && !Array.isArray(b));\n}","tryCatchPattern":null,"preventionTips":["Button keys map to objects with label/className/callback","Use {label: 'Yes'} not 'Yes' as the entry","Validate programmatically generated button maps"],"tags":["bootbox","dialog","buttons","validation"],"backgroundTag":null,"analyzedSha":"e815e74d4c2dd74edac831241f1253fcc7d25381","analyzedAt":"2026-08-14T14:22:34.512Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}