{"record":{"id":"8cd311c52683dd5c","repo":"GoogleChrome/lighthouse","slug":"pluginname-groups-json-is-not-defined-as-an-obj","errorCode":null,"errorMessage":"${pluginName} groups json is not defined as an object.","messagePattern":"(.+?) groups json is not defined as an object\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/config/config-plugin.js","lineNumber":190,"sourceCode":"      manualDescription: manualDescription,\n      supportedModes,\n    };\n  }\n\n\n  /**\n   * Extract and validate groups JSON added by the plugin.\n   * @param {unknown} groupsJson\n   * @param {string} pluginName\n   * @return {Record<string, LH.Config.GroupJson>|undefined}\n   */\n  static _parseGroups(groupsJson, pluginName) {\n    if (groupsJson === undefined) {\n      return undefined;\n    }\n\n    if (!isObjectOfUnknownProperties(groupsJson)) {\n      throw new Error(`${pluginName} groups json is not defined as an object.`);\n    }\n\n    const groups = Object.entries(groupsJson);\n\n    /** @type {Record<string, LH.Config.GroupJson>} */\n    const parsedGroupsJson = {};\n    groups.forEach(([groupId, groupJson]) => {\n      if (!isObjectOfUnknownProperties(groupJson)) {\n        throw new Error(`${pluginName} has a group not defined as an object.`);\n      }\n      const {title, description, ...invalidRest} = groupJson;\n      assertNoExcessProperties(invalidRest, pluginName, 'group');\n\n      if (!i18n.isStringOrIcuMessage(title)) {\n        throw new Error(`${pluginName} has an invalid group title.`);\n      }\n      if (!i18n.isStringOrIcuMessage(description) && description !== undefined) {\n        throw new Error(`${pluginName} has an invalid group description.`);","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/core/config/config-plugin.js#L172-L208","documentation":"Thrown by ConfigPlugin._parseGroups when a plugin defines a 'groups' key whose value is not a plain object (isObjectOfUnknownProperties fails: must be typeof 'object', not null, not an array). groups must be a map of groupId -> groupJson. undefined is allowed (groups omitted); everything else non-object is not.","triggerScenarios":"Plugin exports groups: [...] (an array) or groups: null or groups: 'groupA'. _parseGroups sees it is not undefined and not a plain object, so config-plugin.js:190 throws.","commonSituations":"Confusing groups (a keyed map) with an array. Setting groups: null to mean 'no groups'. JSON config where groups was written as a list.","solutions":["Define groups as an object keyed by group id: groups: {a: {title:'A'}, b: {title:'B'}}.","Remove the groups key entirely if the plugin has no groups."],"exampleFix":"// before\nmodule.exports = {groups: [{title:'A'}], category:{...}};\n// after\nmodule.exports = {groups: {groupA: {title:'A'}}, category:{...}};","handlingStrategy":"validation","validationCode":"function isPlainObject(v) { return typeof v === 'object' && v !== null && !Array.isArray(v); }\nif (plugin.groups !== undefined && !isPlainObject(plugin.groups)) {\n  throw new TypeError('groups must be an object map of groupId -> group');\n}","typeGuard":"function isGroupMap(v) {\n  if (v === undefined) return true;\n  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;\n  return Object.values(v).every(g => typeof g === 'object' && g !== null && !Array.isArray(g));\n}","tryCatchPattern":"try { await ConfigPlugin.parsePlugin(plugin, name); }\ncatch (e) { if (/groups json is not defined as an object/.test(e.message)) console.error('Make groups a keyed object'); throw e; }","preventionTips":["Model groups as {id: {title,...}}, never as an array.","Omit groups when the plugin has none."],"tags":["config","lighthouse","plugin","validation","groups"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}