TS2339 when deploying to Amplify, but same code works in sandbox

I made an initial Amplify app by following the official Amplify Gen 2 walkthrough. I got it to work in the sandbox. It then fails to deploy.

To recreate the error:

  1. Use a freshly created Cloud9 environment (hint: use an upgraded EC2 instance, the free tier won’t work, needs more RAM and disk), combined with a newly bootstrapped Amplify app and a new CodeCommit repository – everything clean.
  2. Follow these instructions – https://docs.amplify.aws/gen2/start/quickstart/vite-react-app/
  3. Work through all the bugs in the instructions (beyond the scope of this question) and get the system to work locally in a sandbox.
  4. Get the whole thing loaded into a Git repository (in this case, CodeCommit) and link the Amplify app to the repository.
  5. Run the deployment (manually or automatic upon a Git push, if everything is working).
    Deployment fails.

Default options and settings and versions are used in all cases, unless specifically mentioned in the linked walkthrough.

The resulting error (in the deployment console) looks like this:

 170 2024-05-01T22:08:22.641Z [INFO]: src/components/TodoList.tsx(14,14): error TS2345: Argument of type '{ readonly id: string; readonly createdAt: string; readonly updatedAt: string; content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; owner?: string | undefined; }[]' is not assignable to parameter of type 'SetStateAction<{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }[]>'.
 171 Type '{ readonly id: string; readonly createdAt: string; readonly updatedAt: string; content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; owner?: string | undefined; }[]' is not assignable to type '{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }[]'.
 172 Property 'type' is missing in type '{ readonly id: string; readonly createdAt: string; readonly updatedAt: string; content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; owner?: string | undefined; }' but required in type '{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }'.
 173 2024-05-01T22:08:22.645Z [INFO]: src/components/TodoList.tsx(20,13): error TS2345: Argument of type '{ readonly id: string; readonly createdAt: string; readonly updatedAt: string; content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; owner?: string | undefined; }[]' is not assignable to parameter of type 'SetStateAction<{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }[]>'.
 174 Type '{ readonly id: string; readonly createdAt: string; readonly updatedAt: string; content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; owner?: string | undefined; }[]' is not assignable to type '{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }[]'.
 175 Type '{ readonly id: string; readonly createdAt: string; readonly updatedAt: string; content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; owner?: string | undefined; }' is not assignable to type '{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }'.
 176 src/components/TodoList.tsx(42,25): error TS2339: Property 'id' does not exist on type '{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }'.
 177 src/components/TodoList.tsx(42,35): error TS2339: Property 'content' does not exist on type '{ type: { readonly id: string; readonly createdAt: string; readonly updatedAt: string; } & { content?: Nullable<string> | undefined; done?: Nullable<boolean> | undefined; priority?: "low" | ... 3 more ... | undefined; } & { ...; }; }'.
 178 2024-05-01T22:08:22.710Z [ERROR]: !!! Build failed
 179 2024-05-01T22:08:22.711Z [ERROR]: !!! Error: Command failed with exit code 2

TodoList.tsx is a direct copy and paste from the tutorial:

import { useState, useEffect } from "react";
import { generateClient } from "aws-amplify/data";
import type { Schema } from "../../amplify/data/resource";

export default function TodoList() {
  // generate your data client using the Schema from your backend
  const client = generateClient<Schema>();

  const [todos, setTodos] = useState<Schema["Todo"][]>([]);

  async function listTodos() {
    // fetch all todos
    const { data } = await client.models.Todo.list();
    setTodos(data);
  }

  useEffect(() => {
  listTodos()
  const sub = client.models.Todo.observeQuery().subscribe(({ items }) =>
   setTodos([...items])
  );

  return () => sub.unsubscribe();
}, []);

  return (
    <div>
      <h1>Todos</h1>
      <button onClick={async () => {
        // create a new Todo with the following attributes
        const { errors, data: newTodo } = await client.models.Todo.create({
          // prompt the user to enter the title
          content: window.prompt("title"),
          done: false,
          priority: 'medium'
        })
        console.log(errors, newTodo);
      }}>Create</button>

      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.content}</li>
        ))}
      </ul>
    </div>
  );
}

../../amplify/data/resource is directly from AWS and looks like this:

import { type ClientSchema, a, defineData } from '@aws-amplify/backend';

/*== STEP 1 ===============================================================
The section below creates a Todo database table with a "content" field. Try
adding a new "isDone" field as a boolean. The authorization rule below
specifies that any user authenticated via an API key can "create", "read",
"update", and "delete" any "Todo" records.
=========================================================================*/

const schema = a.schema({
  Todo: a
    .model({
      content: a.string(),
    })
    .authorization((allow) => [allow.guest()]),
});

export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'iam',
  },
});

/*== STEP 2 ===============================================================
Go to your frontend source code. From your client-side code, generate a
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
WORK IN THE FRONTEND CODE FILE.)
Using JavaScript or Next.js React Server Components, Middleware, Server 
Actions or Pages Router? Review how to generate Data clients for those use
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/

=========================================================================*/

/*
"use client"
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";
const client = generateClient<Schema>() // use this Data client for CRUDL requests
*/

/*== STEP 3 ===============================================================
Fetch records from the database and use them in your frontend component.
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
=========================================================================*/

/* For example, in a React component, you can use this snippet in your
  function's RETURN statement */

// const { data: todos } = await client.models.Todo.list()
// return <ul>{todos.map(todo => <li key={todo.id}>{todo.content}</li>)}</ul>

For completeness, here is package.json (autogenerated and untouched):

{
  "name": "lpm-react-amplify-gen2",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "aws-amplify": "^6.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@aws-amplify/backend": "^0.15.0",
    "@aws-amplify/backend-cli": "^0.15.0",
    "@types/react": "^18.2.66",
    "@types/react-dom": "^18.2.22",
    "@typescript-eslint/eslint-plugin": "^7.2.0",
    "@typescript-eslint/parser": "^7.2.0",
    "@vitejs/plugin-react": "^4.2.1",
    "aws-cdk": "^2.139.1",
    "aws-cdk-lib": "^2.139.1",
    "constructs": "^10.3.0",
    "esbuild": "^0.20.2",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.6",
    "tsx": "^4.7.3",
    "typescript": "^5.4.5",
    "vite": "^5.2.0"
  }
}

I would provide package-lock.json but it is just short of 20,000 lines. In case it matters, I excluded node_modules from the repository via .gitignore.

Since this all works in the sandbox, it seems like there must be some version difference between all the stuff installed for the sandbox compared to what gets deployed and used to build.

One problem I solved (as part of step 2, above) was the failure of npx amplify sandbox, it was segfaulting and core dumping. This is apparently due to an issue with Node 20.12, so you must fall back to 20.11. I thought that might be the source of the issue, so I added some commands to do that fallback in the amplify.yml (Amplify app->Hosting->build options), with no success. I did not try doing the same thing by specifying a custom build container, as that seems redundant and unnecessary if the amplify.yml approach didn’t help. It was using the 20.11 version in the deployment during that test, according to the console printout.

For what it’s worth, the Cloud9 IDE is littered with warnings, mostly they seem to be similar type errors. An alleged fix for this is to change the Typescript version setting in the IDE to match the command-line/sandbox enviroment, but there doesn’t seem to be such a setting (most people only talk about using VSCode, not Cloud9). Regardless of these warnings (errors?) being left unresolved, the Amplify sandbox eventually worked as intended.

At this point, debugging this requires lots of iteration over random ideas and a >5 minute wait for the deployment to fail on each of those iterations. I could really use some focused ideas and direction.

  • What is the likely culprit?
  • Why would the sandbox differ from the deployment environment?

I am not sure if I need to be focusing on syntax problems in the code provided by the tutorial (there were others that had to be solved to get the sandboxed version to work) or if it’s something in the configuration of the deployment or something else entirely.

  • Is it a syntax error in the specified file? If so, why did it work in the sandbox?
  • Is it a language/Node/Typescript version issue?
  • Is it a package version issue?
  • Is it some other version issue?
  • Is it a problem in the Amplify deployment configuration?
  • Is it some other Amplify configuration issue?

How to I get the sandbox to actually align with the deployment so any errors show up locally?

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật