{"record":{"id":"4017d16fa1f19832","repo":"GoogleChrome/lighthouse","slug":"pluginname-has-an-invalid-category-manualdescri","errorCode":null,"errorMessage":"${pluginName} has an invalid category manualDescription.","messagePattern":"(.+?) has an invalid category manualDescription\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/config/config-plugin.js","lineNumber":158,"sourceCode":"    const {\n      title,\n      description,\n      manualDescription,\n      auditRefs: auditRefsJson,\n      supportedModes,\n      ...invalidRest\n    } = categoryJson;\n\n    assertNoExcessProperties(invalidRest, pluginName, 'category');\n\n    if (!i18n.isStringOrIcuMessage(title)) {\n      throw new Error(`${pluginName} has an invalid category tile.`);\n    }\n    if (!i18n.isStringOrIcuMessage(description) && description !== undefined) {\n      throw new Error(`${pluginName} has an invalid category description.`);\n    }\n    if (!i18n.isStringOrIcuMessage(manualDescription) && manualDescription !== undefined) {\n      throw new Error(`${pluginName} has an invalid category manualDescription.`);\n    }\n    if (!isArrayOfGatherModes(supportedModes) && supportedModes !== undefined) {\n      throw new Error(\n        `${pluginName} supportedModes must be an array, ` +\n        `valid array values are \"navigation\", \"timespan\", and \"snapshot\".`\n      );\n    }\n    const auditRefs = ConfigPlugin._parseAuditRefsList(auditRefsJson, pluginName);\n\n    return {\n      title,\n      auditRefs,\n      description: description,\n      manualDescription: manualDescription,\n      supportedModes,\n    };\n  }\n","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/core/config/config-plugin.js#L140-L176","documentation":"Thrown by ConfigPlugin._parseCategory while validating a plugin category's 'manualDescription' field. Like description, manualDescription is optional but, when present, must satisfy i18n.isStringOrIcuMessage (a string or ICU message). It is the text shown for manual categories in the report. Any other type fails.","triggerScenarios":"A plugin category sets manualDescription to a non-string value, e.g. category: {title, manualDescription: ['step 1','step 2'], auditRefs:[...]}. Because it is not undefined and not a string/IcuMessage, config-plugin.js:158 throws.","commonSituations":"Treating manualDescription as an array of steps (it must be a single string). Setting it to an object or number. Migrating from an older plugin schema and leaving a stray value.","solutions":["Set manualDescription to a single string combining your manual steps, e.g. manualDescription: 'Step 1. ... Step 2. ...'","Use an i18n ICU message object if the text needs localization.","Remove the manualDescription key if you do not need manual instructions."],"exampleFix":"// before\ncategory: {title: 'Manual', manualDescription: ['Run axe','Check contrast'], auditRefs: [...]}\n// after\ncategory: {title: 'Manual', manualDescription: 'Run axe. Then check contrast manually.', auditRefs: [...]}","handlingStrategy":"validation","validationCode":"const i18n = require('lighthouse/core/lib/i18n/i18n.js');\nif (cat.manualDescription !== undefined && !i18n.isStringOrIcuMessage(cat.manualDescription)) {\n  throw new TypeError('manualDescription must be a single string');\n}","typeGuard":"function isValidManualDescription(v) {\n  return v === undefined || typeof v === 'string';\n}","tryCatchPattern":"try {\n  await ConfigPlugin.parsePlugin(plugin, name);\n} catch (e) {\n  if (/invalid category manualDescription/.test(e.message)) console.error('manualDescription must be one string');\n  throw e;\n}","preventionTips":["Write manualDescription as a single concatenated string, not an array of steps.","Validate optional category fields as string-or-omitted in your plugin tests."],"tags":["config","lighthouse","plugin","validation","i18n"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}