serverless/serverless · error · ServerlessError

IAM_CREATE_ROLE_FAILED

IAM_CREATE_ROLE_FAILED

Error message

Failed to create role ${roleName}

What it means

Thrown by IAM at iam.js:141 when CreateRole resolves without Role.Arn. The role is created for the events.amazonaws.com service principal with an sts:AssumeRole trust policy. A missing ARN on a resolved response is an SDK contract violation — real CreateRole failures (invalid AssumeRolePolicyDocument, name conflict, path issues) throw as AWS errors before this branch. Note: the { stack: false } option is accepted but ignored by ServerlessError's constructor.

Source

Thrown at packages/engine/src/lib/aws/iam.js:141

      new CreateRoleCommand({
        RoleName: roleName,
        AssumeRolePolicyDocument: JSON.stringify({
          Version: '2012-10-17',
          Statement: [
            {
              Effect: 'Allow',
              Principal: {
                Service: 'events.amazonaws.com',
              },
              Action: 'sts:AssumeRole',
            },
          ],
        }),
      }),
    )

    if (!createRoleResponse.Role?.Arn) {
      throw new ServerlessError(
        `Failed to create role ${roleName}`,
        'IAM_CREATE_ROLE_FAILED',
        {
          stack: false,
        },
      )
    }

    const putRolePolicyResponse = await this.client.send(
      new PutRolePolicyCommand({
        RoleName: roleName,
        PolicyName: 'EventBridgeApiTargetPolicy',
        PolicyDocument: JSON.stringify({
          Version: '2012-10-17',
          Statement: [
            {
              Effect: 'Allow',
              Action: ['events:InvokeApiDestination'],

View on GitHub (pinned to b9d7ea51c8)

Solutions

  1. Validate the AssumeRolePolicyDocument JSON (Version + Statement[].Principal.Service = 'events.amazonaws.com') before the call.
  2. Pin @aws-sdk/client-iam consistently across the engine.
  3. Log createRoleResponse.$metadata.httpStatusCode to confirm a 200 with empty body.
  4. Retry once for transient malformed responses.

Example fix

// before
throw new ServerlessError(`Failed to create role ${roleName}`, 'IAM_CREATE_ROLE_FAILED', { stack: false })

// after
throw new ServerlessError(
  `CreateRole '${roleName}' returned no ARN (status=${createRoleResponse.$metadata?.httpStatusCode})`,
  'IAM_CREATE_ROLE_FAILED',
)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the assume-role policy document before CreateRole
function assertAssumeRolePolicy(doc) {
  if (!doc?.Version) throw new Error('AssumeRolePolicyDocument.Version required')
  const stmt = doc.Statement?.[0]
  if (!stmt || stmt.Principal?.Service !== 'events.amazonaws.com' || stmt.Action !== 'sts:AssumeRole') {
    throw new Error('Statement[0] must allow events.amazonaws.com to sts:AssumeRole')
  }
}

Type guard

function hasRoleArn(res) {
  return Boolean(res?.Role?.Arn)
}

Try / catch

try {
  return await iam.createRoleForEventBridgeAPITarget(name)
} catch (e) {
  if (e.code === 'IAM_CREATE_ROLE_FAILED') {
    throw new Error(`CreateRole returned no ARN; validate AssumeRolePolicyDocument. RequestId=${e.requestId}`)
  }
  throw e
}

Prevention

When it happens

Trigger: createRoleResponse.Role is undefined or Role.Arn is missing on a resolved promise. Causes: @aws-sdk/client-iam output-shape change, proxy stripping the body, AWS-side regression. Malformed AssumeRolePolicyDocument JSON normally throws MalformedPolicyDocumentException first.

Common situations: SDK upgrade changing the CreateRoleResponse shape; corporate MITM proxy; rare AWS regional incident. Often misread as 'role creation failed' — actual creation errors throw.

Related errors


AI-assisted analysis of serverless/serverless@b9d7ea51c8 (2026-08-13). Data as JSON: /api/errors/178095a936bf7784. Report an issue: GitHub.