Error while hydrating

Hi team :wave:, after updating to v5 (now on 5.2.1), I’ve starting getting errors in my app which look to be related to SSR. I’ve recorded a quick demo, most routes render no problem while navigating the app, but if I refresh a page nothing renders and get the below errors.


Strive Bug - Watch Video

Uncaught TypeError: Cannot read properties of undefined (reading 'call')
    at c (runtime-app.0f51d3c3.js:1:157)
    at Object.prerenderLoader (app.c9f44c15.js:2:123198)
    at t.ActiveRouteLoader (app.c9f44c15.js:2:173873)
    at _a (app.c9f44c15.js:2:635427)
    at _u (app.c9f44c15.js:2:692259)
    at wc (app.c9f44c15.js:2:681411)
    at gc (app.c9f44c15.js:2:681339)
    at yc (app.c9f44c15.js:2:681202)
    at ic (app.c9f44c15.js:2:676338)
    at k (app.c9f44c15.js:172:15448)

and

app.c9f44c15.js:2 Uncaught Error: Minified React error #423; visit https://reactjs.org/docs/error-decoder.html?invariant=423 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

Error from the linked react docs:

There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

Ref: https://legacy.reactjs.org/docs/error-decoder.html/?invariant=423

Hi @hamishirving. I’ll take a look at that.

Big thanks to @Tobbe for solving this one.

For anyone with an older project deployed on Netlify (you setup Netlify pre Feb '22), check your netlify.toml

Ref: Always generate 200.html as part of web build | Change by dac09 · Pull Request #4782 · redwoodjs/redwood · GitHub

Redirects section should be as below:

[[redirects]]
  from = "/*"
  to = "/200.html" # not "/index.html"
  status = 200
2 Likes

Just had a similar issue as well with render.com. The solution is once again to redirect to 200.html not index.html as indicated. Just want to clarify that this in not only a Netlify solution. Thanks for posting the solution!

Same issue with the deprecated serverless deploy… thankfully I found this thread!

I had to patch serverless-lift:

diff --git a/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js b/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js
index fe1f34198051bca8c00718db65897ab87ad591c4..9b9c3ba18b45507ada5a5ecf8815b0a55d550022 100644
--- a/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js
+++ b/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js
@@ -269,7 +269,9 @@ See https://github.com/getlift/lift/blob/master/docs/static-website.md#custom-do
       httpStatus: 404,
       ttl: import_aws_cdk_lib.Duration.seconds(0),
       responseHttpStatus: 200,
-      responsePagePath: "/index.html"
+      // don't use index.html because that's specifically for the home page...
+      // instead, route unknown pages to 200.html which bootstraps redwood
+      responsePagePath: "/200.html"
     };
   }
   createResponseFunction() {

I also have other patches to serverless-lift in order to fix bucket policy issues, and prevent it from deleting old files. I think I’m the only one out there still using this lol.

Hey @sean-ac , do you have a fork of serverless-lift? how’d you patch it?

Actually, a patch may not be necessary - See this lift/docs/static-website.md at master · getlift/lift · GitHub

Follow up on this, I’m not able to use the proposed change to fix it. @sean-ac can you share a little more detail on the fix you made?

sure, here’s my full patch:

diff --git a/dist/src/constructs/aws/StaticWebsite.js b/dist/src/constructs/aws/StaticWebsite.js
index 6cf066af9bba8131b82ec103d2b76c7da134876d..824523b8748cee349c9f04202801c7c0fd017a76 100644
--- a/dist/src/constructs/aws/StaticWebsite.js
+++ b/dist/src/constructs/aws/StaticWebsite.js
@@ -34,6 +34,7 @@ module.exports = __toCommonJS(StaticWebsite_exports);
 var cloudfront = __toESM(require("aws-cdk-lib/aws-cloudfront"));
 var import_aws_cloudfront = require("aws-cdk-lib/aws-cloudfront");
 var import_aws_cdk_lib = require("aws-cdk-lib");
+var import_aws_s3 = require("aws-cdk-lib/aws-s3");
 var import_cloudfrontFunctions = require("../../classes/cloudfrontFunctions");
 var import_getDefaultCfnFunctionAssociations = require("../../utils/getDefaultCfnFunctionAssociations");
 var import_naming = require("../../utils/naming");
@@ -84,7 +85,15 @@ class StaticWebsite extends import_StaticWebsiteAbstract.StaticWebsiteAbstract {
       // public read access is required when enabling static website hosting
       publicReadAccess: true,
       // For a static website, the content is code that should be versioned elsewhere
-      removalPolicy: import_aws_cdk_lib.RemovalPolicy.DESTROY
+      removalPolicy: import_aws_cdk_lib.RemovalPolicy.DESTROY,
+
+      // allow access to public web bucket
+      blockPublicAccess: new import_aws_s3.BlockPublicAccess({
+        blockPublicAcls: false,
+        blockPublicPolicy: false,
+        ignorePublicAcls: false,
+        restrictPublicBuckets: false
+      })
     };
   }
 }
diff --git a/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js b/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js
index fe1f34198051bca8c00718db65897ab87ad591c4..9b9c3ba18b45507ada5a5ecf8815b0a55d550022 100644
--- a/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js
+++ b/dist/src/constructs/aws/abstracts/StaticWebsiteAbstract.js
@@ -269,7 +269,9 @@ See https://github.com/getlift/lift/blob/master/docs/static-website.md#custom-do
       httpStatus: 404,
       ttl: import_aws_cdk_lib.Duration.seconds(0),
       responseHttpStatus: 200,
-      responsePagePath: "/index.html"
+      // don't use index.html because that's specifically for the home page...
+      // instead, route unknown pages to 200.html which bootstraps redwood
+      responsePagePath: "/200.html"
     };
   }
   createResponseFunction() {
diff --git a/dist/src/utils/s3-sync.js b/dist/src/utils/s3-sync.js
index 54f69f3978518268aa2fea5d4ae3fe461228396f..de820beed94229b1dc9ba5671c6c3a152fd3e2f2 100644
--- a/dist/src/utils/s3-sync.js
+++ b/dist/src/utils/s3-sync.js
@@ -53,6 +53,12 @@ async function s3Sync({
   let fileChangeCount = 0;
   const filesToUpload = await listFilesRecursively(localPath);
   const existingS3Objects = await s3ListAll(aws, bucketName, targetPathPrefix);
+
+  // don't delete any files... why would we delete files?
+  for (const key of Object.keys(existingS3Objects)) {
+    delete existingS3Objects[key];
+  }
+
   let skippedFiles = 0;
   for (const batch of (0, import_lodash.chunk)(filesToUpload, 2)) {
     await Promise.all(

This is in the file .yarn/patches/serverless-lift-npm-1.28.1-5d57d22b94.patch – not sure if you have the same exact version.

I generated this using yarn’s built-in patch system. If I recall correctly, I did something like yarn patch serverless-lift, and yarn copies the code into a temporary directory. Then I edited the necessary files, and told yarn to finish the patch.