{"record":{"id":"8e1dd707ce09492c","repo":"GoogleChrome/lighthouse","slug":"pluginname-has-an-invalid-group-title","errorCode":null,"errorMessage":"${pluginName} has an invalid group title.","messagePattern":"(.+?) has an invalid group title\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/config/config-plugin.js","lineNumber":205,"sourceCode":"    }\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.`);\n      }\n      parsedGroupsJson[`${pluginName}-${groupId}`] = {\n        title,\n        description,\n      };\n    });\n    return parsedGroupsJson;\n  }\n\n  /**\n   * Extracts and validates a config from the provided plugin input, throwing\n   * if it deviates from the expected object shape.\n   * @param {unknown} pluginJson\n   * @param {string} pluginName\n   * @return {LH.Config}","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/core/config/config-plugin.js#L187-L223","documentation":"Thrown by ConfigPlugin._parseGroups after confirming a group is an object. Each group requires a 'title' that is a string or ICU message (i18n.isStringOrIcuMessage). Unlike description, title is mandatory; a missing or non-string title fails the check (no undefined escape, because isStringOrIcuMessage(undefined) is false).","triggerScenarios":"groups: {groupA: {description: 'desc'}} (no title) or groups: {groupA: {title: 5}}. isStringOrIcuMessage(title) is false, so config-plugin.js:205 throws.","commonSituations":"Forgetting the title field (it is required, unlike description). Setting title to a number or object. Assuming description alone is enough.","solutions":["Add a string title to every group: {title: 'Group A'}.","Use an i18n ICU message for localizable titles.","Ensure no group object omits title."],"exampleFix":"// before\ngroups: {a: {description: 'only desc'}}\n// after\ngroups: {a: {title: 'Group A', description: 'only desc'}}","handlingStrategy":"validation","validationCode":"const i18n = require('lighthouse/core/lib/i18n/i18n.js');\nfor (const [id, g] of Object.entries(plugin.groups || {})) {\n  if (!i18n.isStringOrIcuMessage(g.title)) throw new TypeError(`group '${id}' requires a string title`);\n}","typeGuard":"function groupHasTitle(g) { return typeof g === 'object' && g !== null && typeof g.title === 'string'; }","tryCatchPattern":"try { await ConfigPlugin.parsePlugin(plugin, name); }\ncatch (e) { if (/invalid group title/.test(e.message)) console.error('Add a string title to each group'); throw e; }","preventionTips":["title is mandatory on every group (description is optional).","Add a plugin unit test that asserts every group has a string title."],"tags":["config","lighthouse","plugin","validation","groups","i18n"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}