Skip to content

Amplify Gen 2 AppSync "Function Not Found" for Lambda Resolver Despite Alias and Permissions

0

Hi AWS Community, I'm encountering a persistent ResourceNotFoundException in my Amplify Gen 2 project when AppSync tries to resolve a GraphQL mutation by invoking a Lambda function. The error specifically states Function not found: arn:aws:lambda:us-east-2:ACCOUNT_ID:function:stripeCheckout, even though the Lambda exists, has an alias named "stripeCheckout", and I believe permissions are correctly set. Project Setup: Framework: AWS Amplify Gen 2 (using TypeScript with CDK for backend definitions) Region: us-east-2 Goal: Integrate Stripe for subscriptions. The createStripeCheckoutSession mutation is failing. Error Message from Client (via AppSync):

{
  "data": {
    "createStripeCheckoutSession": null
  },
  "errors": [
    {
      "path": [
        "createStripeCheckoutSession"
      ],
      "data": null,
      "errorType": "Lambda:IllegalArgument",
      "errorInfo": null,
      "locations": [/* ... */],
      "message": "software.amazon.awssdk.services.lambda.model.ResourceNotFoundException: Function not found: arn:aws:lambda:us-east-2:970547380883:function:stripeCheckout (Service: Lambda, Status Code: 404, Request ID: <some-uuid>) (SDK Attempt Count: 1)"
    }
  ]
}

Relevant amplify/data/resource.ts (GraphQL Schema Definition):

import { a, defineData } from "@aws-amplify/backend";

const dataSchema = a.schema({
  // ... other models ...

  CreateStripeCheckoutSessionInput: a.customType({
    subscriptionTierId: a.string().required(),
    successUrl: a.string().required(),
    cancelUrl: a.string().required(),
  }),

  StripeCheckoutSessionPayload: a.customType({
    sessionId: a.string(),
    sessionUrl: a.string(),
    error: a.string(),
  }),

  // ... other types and models ...

  createStripeCheckoutSession: a
    .mutation()
    .arguments({ input: a.ref("CreateStripeCheckoutSessionInput").required() })
    .returns(a.ref("StripeCheckoutSessionPayload"))
    .authorization((allow) => [allow.authenticated()])
    .handler(a.handler.function("stripeCheckout")), // <--- Logical handler name

  // Example of another (working) function handler:
  getCurrentUserSubscription: a
    .query()
    .returns(a.ref("UserSubscription"))
    .authorization((allow) => [allow.authenticated()])
    .handler(a.handler.function("getUserSubFunc")),

  // ... other mutations/queries ...
});

export const data = defineData({
  schema: dataSchema,
  authorizationModes: { /* ... */ }
});

Relevant amplify/backend.ts (CDK Backend Definition):

import { defineBackend } from "@aws-amplify/backend";
import { data } from "./data/resource"; // Imports the schema above
import { stripeCheckoutFunc } from "./functions/stripeCheckout/resource"; // Definition of the Lambda
import { getCurrentUserSubscriptionFunc } from "./functions/getCurrentUserSubscription/resource";
// ... other function imports ...
import { Alias, CfnPermission } from "aws-cdk-lib/aws-lambda";
import { Stack } from "aws-cdk-lib";

const backend = defineBackend({
  data: data,
  stripeCheckoutFunc: stripeCheckoutFunc,
  getCurrentUserSubscriptionFunc: getCurrentUserSubscriptionFunc,
  // ... other functions ...
});

const dataStack = Stack.of(backend.data as any);

// Alias for stripeCheckoutFunc
const stripeCheckoutFuncAlias = new Alias(dataStack, "stripeCheckoutFuncAlias", {
  aliasName: "stripeCheckout", // Alias is NAMED "stripeCheckout"
  version: backend.stripeCheckoutFunc.resources.lambda.latestVersion,
});

// Alias for a working function (example)
const getUserSubFuncAlias = new Alias(dataStack, "getUserSubFuncAlias", {
  aliasName: "getUserSubFunc",
  version: backend.getCurrentUserSubscriptionFunc.resources.lambda.latestVersion,
});

// Permission for AppSync to invoke the stripeCheckout ALIAS
new CfnPermission(dataStack, 'StripeCheckoutFuncAliasAppSyncInvoke', {
  action: 'lambda:InvokeFunction',
  principal: 'appsync.amazonaws.com',
  functionName: stripeCheckoutFuncAlias.functionArn, // Using Alias ARN
  sourceArn: backend.data.resources.cfnResources.cfnGraphqlApi.attrArn
});

// Permission for a working function (example)
new CfnPermission(dataStack, 'GetUserSubFuncAppSyncInvoke', {
  action: 'lambda:InvokeFunction',
  principal: 'appsync.amazonaws.com',
  functionName: getUserSubFuncAlias.functionArn, // Using Alias ARN
  sourceArn: backend.data.resources.cfnResources.cfnGraphqlApi.attrArn
});

// ... other permissions and configurations ...

Lambda Function Details: The actual deployed Lambda for Stripe checkout has a physical name like amplify-greeetsamplify-gr-stripeCheckoutlambdaA793-mAz1cJj8xyzt. It has an alias named stripeCheckout which points to $LATEST.

What I've Tried/Verified:

  1. Alias Exists: The "stripeCheckout" alias for the Lambda is correctly created and points to the latest version.
  2. CfnPermission Targets Alias ARN: The CfnPermission (StripeCheckoutFuncAliasAppSyncInvoke) uses stripeCheckoutFuncAlias.functionArn to grant AppSync permission to invoke the specific alias.
  3. Comparison with Working Function: Other functions (e.g., getCurrentUserSubscriptionFunc with alias getUserSubFunc) are defined with a similar pattern in data/resource.ts (using a logical name like "getUserSubFunc" in a.handler.function()) and backend.ts (alias creation and CfnPermission targeting the alias ARN), and they work correctly. AppSync seems to resolve their logical names to the correct alias ARNs.
  4. No Typos: The logical name "stripeCheckout" in a.handler.function("stripeCheckout") matches the aliasName: "stripeCheckout" for stripeCheckoutFuncAlias.

The Puzzle: Why is AppSync attempting to invoke arn:aws:lambda:us-east-2:ACCOUNT_ID:function:stripeCheckout (using the short, logical name as the function's physical name) instead of the actual physical Lambda ARN or, preferably, the stripeCheckoutFuncAlias.functionArn for the createStripeCheckoutSession mutation? This behavior is inconsistent with other working Lambda resolvers in the same project. Is there an edge case or a specific nuance in Amplify Gen 2's a.handler.function("logicalName") resolution when the logical name might be too simple, or perhaps when it exactly matches an alias name, that could cause it to form an incorrect ARN for the AppSync data source? How can I ensure Amplify correctly configures the AppSync data source for createStripeCheckoutSession to use the stripeCheckoutFuncAlias.functionArn?

Any insights or suggestions on how to debug or resolve this AppSync data source misconfiguration within the Amplify Gen 2 framework would be greatly appreciated!

1 Answer
0
Based on your description, it appears you're encountering an issue with AWS AppSync trying to invoke a Lambda function using an incorrect ARN format. This is likely related to how AppSync resolves the function name in your GraphQL resolver configuration.

When you use `a.handler.function("stripeCheckout")` in your schema definition, AppSync is looking for a Lambda function with the exact name "stripeCheckout" rather than resolving to your Lambda alias. This is why you're seeing the error message indicating that the function `arn:aws:lambda:us-east-2:ACCOUNT_ID:function:stripeCheckout` cannot be found.

In AWS AppSync, when configuring Lambda resolvers, the system needs to know the exact ARN of the Lambda function or alias to invoke. The logical name you provide in `a.handler.function()` needs to map correctly to the actual Lambda resource.

Here are some potential solutions:

1. Make sure the Lambda function's name or alias is correctly referenced in your AppSync configuration. The function name in your handler should match how AppSync is configured to find that function.

2. Check if there's a mismatch between how the Lambda data source is defined in AppSync and how you're referencing it in your resolver. AppSync needs to have a data source that correctly points to your Lambda function or alias.

3. Verify that the IAM permissions are correctly set up not just for the Lambda function but also for the specific alias you're trying to use. The permission should explicitly allow AppSync to invoke the function using the alias ARN.

4. Consider checking if there's a specific way in Amplify Gen 2 to reference Lambda aliases in GraphQL resolvers that differs from your current approach.

5. If you're using pipeline resolvers, ensure that each function in the pipeline is correctly configured to use the appropriate Lambda function or alias.

Since you mentioned that other Lambda resolvers are working correctly in your project, it might be helpful to compare the configurations between the working resolvers and the problematic one to identify any subtle differences.
  

Sources
AWS::Serverless::GraphQLApi - AWS Serverless Application Model
Using AWS Lambda resolvers in AWS AppSync - AWS AppSync
AWS AppSync JavaScript resolver function reference for Lambda - AWS AppSync
AWS AppSync resolver mapping template reference for Lambda - AWS AppSync

answered 9 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.