{"record":{"id":"6939d4fe9972887e","repo":"serverless/serverless","slug":"cloudwatch-describe-log-groups-error","errorCode":"CLOUDWATCH_DESCRIBE_LOG_GROUPS_ERROR","errorMessage":"Failed to describe log groups: ${error.message}","messagePattern":"Failed to describe log groups: (.+?)","errorType":"exception","errorClass":"ServerlessError","httpStatus":null,"severity":"error","filePath":"packages/engine/src/lib/aws/cloudwatch.js","lineNumber":156,"sourceCode":"   * Describes log groups in CloudWatch Logs\n   *\n   * @param {Object} params - Parameters for the operation\n   * @param {string} [params.logGroupNamePrefix] - The prefix to match\n   * @param {number} [params.limit] - The maximum number of log groups to return\n   * @returns {Promise<Object>} - The response from the DescribeLogGroups operation\n   * @throws {ServerlessError} If fetching log groups fails\n   */\n  describeLogGroups = async ({ logGroupNamePrefix, limit } = {}) => {\n    try {\n      const command = new DescribeLogGroupsCommand({\n        logGroupNamePrefix,\n        limit,\n      })\n\n      return await this.logsClient.send(command)\n    } catch (error) {\n      logger.error(`Error describing log groups: ${error.message}`)\n      throw new ServerlessError(\n        `Failed to describe log groups: ${error.message}`,\n        'CLOUDWATCH_DESCRIBE_LOG_GROUPS_ERROR',\n      )\n    }\n  }\n\n  getRecentLogs = async ({ logGroupName, limit = 20, startTime }) => {\n    if (!logGroupName) {\n      throw new ServerlessError(\n        'Log group name must be provided to fetch logs',\n        'CLOUDWATCH_LOG_GROUP_MISSING',\n      )\n    }\n\n    const now = Date.now()\n    const effectiveStartTime = startTime || now - 5 * 60 * 1000 // Default to the last 5 minutes\n\n    try {","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/serverless/serverless/blob/b9d7ea51c8cce57cff1207964b9b71123673081f/packages/engine/src/lib/aws/cloudwatch.js#L138-L174","documentation":"Wraps any failure from CloudWatch Logs DescribeLogGroupsCommand into a ServerlessError with code CLOUDWATCH_DESCRIBE_LOG_GROUPS_ERROR. describeLogGroups forwards { logGroupNamePrefix, limit } to the logsClient; on AWS SDK failure it logs and re-throws a normalized ServerlessError with the underlying error.message.","triggerScenarios":"describeLogGroups({ logGroupNamePrefix, limit }) when logsClient.send(DescribeLogGroupsCommand) rejects. Typical AWS causes: missing logs:DescribeLogGroups IAM permission, expired credentials, throttling, invalid limit (must be 1-50), or network errors.","commonSituations":"An MCP confirmation-handler flow listing log groups without logs:DescribeLogGroups permission; credentials expiring during a long IDE session; passing a limit > 50 (AWS rejects); wrong region configured so the credentials lack access.","solutions":["Grant logs:DescribeLogGroups in the caller's IAM policy.","Refresh credentials (aws sso login / set a valid profile).","If passing limit, keep it within 1-50.","Verify awsConfig.region is the region where the log groups live."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const res = await cw.describeLogGroups({ logGroupNamePrefix, limit })\n} catch (err) {\n  if (err.code === 'CLOUDWATCH_DESCRIBE_LOG_GROUPS_ERROR') {\n    if (/ExpiredToken/.test(err.message)) throw new Error('AWS credentials expired - refresh profile')\n    if (/AccessDenied/.test(err.message)) throw new Error('Missing logs:DescribeLogGroups permission')\n  }\n  throw err\n}","preventionTips":["Grant logs:DescribeLogGroups in the caller's IAM policy.","Keep limit within AWS's 1-50 range.","Confirm awsConfig.region is correct.","Refresh credentials before long-running IDE/MCP sessions."],"tags":["cloudwatch","logs","aws-sdk","credentials","iam"],"backgroundTag":null,"analyzedSha":"b9d7ea51c8cce57cff1207964b9b71123673081f","analyzedAt":"2026-08-13T04:14:40.386Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}