next-drupal icon indicating copy to clipboard operation
next-drupal copied to clipboard

TypeError: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object

Open demo-Ghost opened this issue 4 years ago • 29 comments

Hi! Thanks for all the work you've put into this project. I'm following the quick start tutorial and have managed to set things up, after a some setbacks with simple_oauth library.

But, currently, I'm having an issue viewing my blog posts. I can see them at the home page and can also see that all their content is fetched in home page, as a collection. But once I visit a specific one (blog/first-post, as in blog/[node:title]), I get a 404 in the Network tab in Google Chrome.

And also this error.

 1 of 1 unhandled error

Unhandled Runtime Error
Error: Failed to load script: /_next/static/chunks/pages/404.js

Source
.next\static\chunks\main.js (83:51) @ HTMLScriptElement.script.onerror

  81 | //    executes when `src` is set.
  82 | script.onload = resolve;
> 83 | script.onerror = ()=>reject(markAssetError(new Error(`Failed to load script: ${src}`)))
     |                                           ^
  84 | ;
  85 | // 2. Configure the cross-origin attribute before setting `src` in case the
  86 | //    browser begins to fetch.

I guess the following error is due to not getting any data in the response.

error - TypeError [ERR_INVALID_ARG_TYPE]: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object
    at Function.byteLength (buffer.js:726:11)
    at ServerResponse.apiRes.end (C:\Users\trogl\Desktop\next-next-next\node_modules\next\dist\server\api-utils.js:72:41)
    at C:\Users\trogl\Desktop\next-next-next\node_modules\next-drupal\dist\index.js:566:30
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async Object.apiResolver (C:\Users\trogl\Desktop\next-next-next\node_modules\next\dist\server\api-utils.js:101:9)
    at async DevServer.handleApiRequest (C:\Users\trogl\Desktop\next-next-next\node_modules\next\dist\server\next-server.js:770:9)
    at async Object.fn (C:\Users\trogl\Desktop\next-next-next\node_modules\next\dist\server\next-server.js:661:37)
    at async Router.execute (C:\Users\trogl\Desktop\next-next-next\node_modules\next\dist\server\router.js:205:32)
    at async DevServer.run (C:\Users\trogl\Desktop\next-next-next\node_modules\next\dist\server\next-server.js:841:29) {
  code: 'ERR_INVALID_ARG_TYPE',
  page: '/api/preview'
}

demo-Ghost avatar Oct 12 '21 19:10 demo-Ghost

Can you post the code or part of for the blog post page file?

shadcn avatar Oct 12 '21 19:10 shadcn

I haven't changed anything there ([...slug].tsx), but let me get it for you.

import * as React from "react"
import Head from "next/head"
import {
  getPathsFromContext,
  getResourceFromContext,
  getResourceTypeFromContext,
} from "next-drupal"
import { GetStaticPropsContext } from "next"
import { NodeArticle } from "@/nodes/node-article"
import { NodeBasicPage } from "@/components/nodes/node-basic-page"

/* eslint-disable  @typescript-eslint/no-explicit-any */
interface NodePageProps {
  preview: GetStaticPropsContext["preview"]
  node: Record<string, any>
}

export default function NodePage({ node, preview }: NodePageProps) {
  const [showPreviewAlert, setShowPreviewAlert] = React.useState<boolean>(false)

  if (!node) return null

  React.useEffect(() => {
    setShowPreviewAlert(preview && window.top === window.self)
  }, [])

  return (
    <>
      <Head>
        <title>{node.title}</title>
        <meta
          name="description"
          content="A Next.js site powered by a Drupal backend."
        />
      </Head>
      {showPreviewAlert && (
        <div className="fixed top-4 right-4">
          <a
            href="/api/exit-preview"
            className="bg-black text-white rounded-md px-4 py-2 text-sm"
          >
            Exit preview
          </a>
        </div>
      )}
      {node.type === "node--page" && <NodeBasicPage node={node} />}
      {node.type === "node--article" && <NodeArticle node={node} />}
    </>
  )
}

export async function getStaticPaths(context) {
  const output = {
    paths: await getPathsFromContext(["node--article", "node--page"], context),
    fallback: true,
  };

  return output; 
}

export async function getStaticProps(context) {
  const type = await getResourceTypeFromContext(context);

  if (!type) {
    return {
      notFound: true,
    }
  }

  let params = {}
  if (type === "node--article") {
    params = {
      include: "field_image,uid",
    }
  }

  const node = await getResourceFromContext(type, context, {
    params,
  })

  if (!node?.status) {
    return {
      notFound: true,
    }
  }

  return {
    props: {
      preview: context.preview || false,
      node,
    },
    revalidate: 60,
  }
}

node

demo-Ghost avatar Oct 12 '21 19:10 demo-Ghost

Is this happening in the Drupal preview only? What happens if you visit the post directly?

shadcn avatar Oct 12 '21 19:10 shadcn

You mean from Next.js or in Drupal?

If I go to my Drupal site at localhost and click on the post on Homepage, I get this

image

demo-Ghost avatar Oct 12 '21 19:10 demo-Ghost

I mean if you visit the page on the Next site

shadcn avatar Oct 12 '21 19:10 shadcn

On the Next site, homepage for the default-starter works great. Plus, I see all the data of the collection. image

but if I click on one of the posts, I get this image

demo-Ghost avatar Oct 12 '21 19:10 demo-Ghost

Just to add to this, although it might not be relevant, but my env variable for DRUPAL_FRONT_PAGE(which I really don't know what it does exactly) is /node.

demo-Ghost avatar Oct 12 '21 19:10 demo-Ghost

I'm trying to reproduce this. Can you delete the .next directory and try restarting the server again yarn dev?

Let's also add a console.log(node) in getStaticProps to confirm if Next is able to pull the blog node:

export async function getStaticProps(context) {
  const type = await getResourceTypeFromContext(context);

  if (!type) {
    return {
      notFound: true,
    }
  }

  let params = {}
  if (type === "node--article") {
    params = {
      include: "field_image,uid",
    }
  }

  const node = await getResourceFromContext(type, context, {
    params,
  })

  console.log(node) // <----------- 👈

  if (!node?.status) {
    return {
      notFound: true,
    }
  }

  return {
    props: {
      preview: context.preview || false,
      node,
    },
    revalidate: 60,
  }
}

shadcn avatar Oct 13 '21 08:10 shadcn

I deleted .next and restarted. The node is always null in the console and problem still persists.

I also deleted the next.js sites and pathauto patterns in Drupal yesterday and re-created them in case something was wrong there, but I think the peoblem is on the Next side of things.

Two more things to mention:

  • When first installing (following the next-drupal QuickStart guide), it tells me that next-drupal needs a previous version of Next. I'm running 11.1.2.
  • Visiting localhost:3000/api/preview from the Next.js app, gives me an "Invalid preview secret" message.

demo-Ghost avatar Oct 13 '21 09:10 demo-Ghost

Do you think this could be an issue related to decoupled routing?

demo-Ghost avatar Oct 13 '21 11:10 demo-Ghost

I did some debugging in your source code, adding some console logs in buildUrl and getResourceByPath functions, because I think this has to do with the Url Aliases and Routing. As I mentioned before, the issue is not when fetching a collection of posts, but an individual post, at [...slug].jsx (check basic-starter source code).

What I get is this (sorry for this awful spaghetti sequence :) )

event - build page: /[...slug]
wait  - compiling...
event - compiled successfully
-
url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/jsonapi',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/jsonapi',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}
-
url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/jsonapi',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/jsonapi',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}
-
url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/jsonapi/node/page?fields%5Bnode--page%5D=path&fields%5Bnode--article%5D=path',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/jsonapi/node/page',
  search: '?fields%5Bnode--page%5D=path&fields%5Bnode--article%5D=path',
  searchParams: URLSearchParams { 'fields[node--page]' => 'path', 'fields[node--article]' => 'path' },
  hash: ''
}
-
url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/jsonapi/node/article?fields%5Bnode--article%5D=path',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/jsonapi/node/article',
  search: '?fields%5Bnode--article%5D=path',
  searchParams: URLSearchParams { 'fields[node--article]' => 'path' },
  hash: ''
}
-
url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/router/translate-path?path=blog%2Fsecond-article',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/router/translate-path',
  search: '?path=blog%2Fsecond-article',
  searchParams: URLSearchParams { 'path' => 'blog/second-article' },
  hash: ''
}
-
url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/subrequests?_format=json',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/subrequests',
  search: '?_format=json',
  searchParams: URLSearchParams { '_format' => 'json' },
  hash: ''
}
-
url in getResourceByPath http://localhost/drupal-next/web/subrequests?_format=json
-
response data in getResourceByPath {
  resolved: 'http://localhost/blog/second-article',
  isHomePath: false,
  entity: {
    canonical: 'http://localhost/blog/second-article',
    type: 'node',
    bundle: 'article',
    id: '2',
    uuid: 'afe65deb-6e3c-48a4-9b2b-fc807caf7857'
  },
  label: 'Second Article',
  jsonapi: {
    individual: 'http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857',
    resourceName: 'node--article',
    pathPrefix: 'jsonapi',
    basePath: '/jsonapi',
    entryPoint: 'http://localhost/jsonapi'
  },
  meta: {
    deprecated: {
      'jsonapi.pathPrefix': 'This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead.'
    }
  }
}

Everything looks good, until you check the last console output

response data in getResourceByPath {
  resolved: 'http://localhost/blog/second-article',
  isHomePath: false,
  entity: {
    canonical: 'http://localhost/blog/second-article',
    type: 'node',
    bundle: 'article',
    id: '2',
    uuid: 'afe65deb-6e3c-48a4-9b2b-fc807caf7857'
  },
  label: 'Second Article',
  jsonapi: {
    individual: 'http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857',
    resourceName: 'node--article',
    pathPrefix: 'jsonapi',
    basePath: '/jsonapi',
    entryPoint: 'http://localhost/jsonapi'
  },
  meta: {
    deprecated: {
      'jsonapi.pathPrefix': 'This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead.'
    }
  }
}

The URL is wrong there for jsonapi.individual, it shouldn't be http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857.

I'm not sure if this is some config thing in Drupal, or some config in Next.js. Plus, I also configured my url to be localhost/drupal-next/web after suspecting that it could be an issue with path aliases and also did a clean Drupal install from scratch. Initially, I had Drupal configured at plain http://localhost with some Apache settings (no drupal-next/web), but removed those settings, just to make sure I didn't mess with the paths.

The error in this Github Issue title, TypeError: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object comes from giving the wrong input to a function, it expects a string or buffer, but instead gets an object { message: "Invalid slug" } from within next-drupal source code.

demo-Ghost avatar Oct 14 '21 20:10 demo-Ghost

Few things I've found that may help guide you @shadcn.

I managed to reproduce the issue of a 404 appearing on individual slugs. I was able to rectify the issue by adding 'deserialize: false' to the options parameter of 'getResourceFromContext'.

In the getResourceByPath function, if data is serialised it will return undefined. I have not been able to determine why.

image-1

This then returns the data back to the getStaticProps function, however the data does not contain a node.status (edit: I imagine this is likely due to deserialize not being run), so it will return notFound: true

image-2

Removing this check will create a successful page load.

Obviously this is not ideal and the checks are there for a reason but hopefully this will provide some context in order for you to further investigate.

Edit: Apologies for all the edits.

Seems there's one more thing I forgot to mention. Setting deserialize to false is not enough, this line here also seems to cause null to be returned.

image-3

rilrom avatar Oct 15 '21 02:10 rilrom

@demo-Ghost @rilrom Thanks for the detailed info. Is there anyway you can help me reproduce this (minimal reproducible repo)?

shadcn avatar Oct 18 '21 07:10 shadcn

@shadcn In my case, this is a local environment in Windows, with Xampp. Other than that, just follow the exact instructions on Quick Start guide https://next-drupal.org/docs/quick-start. That's about it I think, the repo is the same as the basic starter.

demo-Ghost avatar Oct 18 '21 08:10 demo-Ghost

I am experiencing the same phenomenon.

TypeError [ERR_INVALID_ARG_TYPE]: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object
    at new NodeError (node:internal/errors:371:5)
    at Function.byteLength (node:buffer:733:11)
    at ServerResponse.apiRes.end (/opt/preview/.docker/preview/node_modules/next/dist/server/api-utils.js:72:41)
    at /opt/preview/.docker/preview/node_modules/next-drupal/dist/index.js:593:30
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.apiResolver (/opt/preview/.docker/preview/node_modules/next/dist/server/api-utils.js:101:9)
    at async Server.handleApiRequest (/opt/preview/.docker/preview/node_modules/next/dist/server/next-server.js:770:9)
    at async Object.fn (/opt/preview/.docker/preview/node_modules/next/dist/server/next-server.js:661:37)
    at async Router.execute (/opt/preview/.docker/preview/node_modules/next/dist/server/router.js:205:32)
    at async Server.run (/opt/preview/.docker/preview/node_modules/next/dist/server/next-server.js:841:29) {
  code: 'ERR_INVALID_ARG_TYPE'

The version I am using is as follows.

  • next-drupal: 0.16.2
  • next.js (Drupal Module): 1.0.0-beta5

I refer to this code, On the Drupal side, the page loaded successfully by applying the following patch to the subrequests module.

        "patches": {
+	    "drupal/subrequests": {
+                "Subrequest failed validation": "https://www.drupal.org/files/issues/2019-05-27/3029570-array-not-object.patch",
+                "Get same results on different request": "https://www.drupal.org/files/issues/2019-07-18/change_request_type-63049395-09.patch"
+            }
        },


taku3202 avatar Oct 18 '21 08:10 taku3202

Just thinking out loud here.

It looks like buildUrl is correctly returning the url but the response from decoupled router has the wrong base url.

url in buildUrl URL {
  href: 'http://localhost/drupal-next/web/subrequests?_format=json', <----------------- ✅
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/drupal-next/web/subrequests',
  search: '?_format=json',
  searchParams: URLSearchParams { '_format' => 'json' },
  hash: ''
}
-
url in getResourceByPath http://localhost/drupal-next/web/subrequests?_format=json
-
response data in getResourceByPath {
  resolved: 'http://localhost/blog/second-article',  <----------------- ❌
  isHomePath: false,
  entity: {
    canonical: 'http://localhost/blog/second-article',  <----------------- ❌
    type: 'node',
    bundle: 'article',
    id: '2',
    uuid: 'afe65deb-6e3c-48a4-9b2b-fc807caf7857'
  },
  label: 'Second Article',
  jsonapi: {
    individual: 'http://localhost/jsonapi/node/article/afe65deb-6e3c-48a4-9b2b-fc807caf7857',
    resourceName: 'node--article',
    pathPrefix: 'jsonapi',
    basePath: '/jsonapi',
    entryPoint: 'http://localhost/jsonapi'  <----------------- ❌
  },
  meta: {
    deprecated: {
      'jsonapi.pathPrefix': 'This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead.'
    }
  }
}

I'll looking into the Drupal/Decoupled Router end.

shadcn avatar Oct 25 '21 09:10 shadcn

Aha

From \Drupal\decoupled_router\EventSubscriber\RouterPathTranslatorSubscriber::onPathTranslation:

$path = $event->getPath();
$path = $this->cleanSubdirInPath($path, $event->getRequest());
/**
   * Removes the subdir prefix from the path.
   *
   * @param string $path
   *   The path that can contain the subdir prefix.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request to extract the path prefix from.
   *
   * @return string
   *   The clean path.
   */
  protected function cleanSubdirInPath($path, Request $request) {
    // Remove any possible leading subdir information in case Drupal is
    // installed under http://example.com/d8/index.php
    $regexp = preg_quote($request->getBasePath(), '/');
    return preg_replace(sprintf('/^%s/', $regexp), '', $path);
  }

Trying to figure out why we're doing this now.

shadcn avatar Oct 25 '21 09:10 shadcn

Hello again @shadcn . I'm trying to think why you could have done this. Something that seemed weird to me, is that URL Aliases at '/admin/config/search/path', image would always show the full url path, regardless of where Drupal is served from. Even if served directly at http://localhost, the URL Aliases would include /drupal/web in the url. image

demo-Ghost avatar Oct 25 '21 13:10 demo-Ghost

This is coming from Drupal. Been a while since I used Drupal in a subdir path. I can't remember if there was a config somewhere to overwrite this.

You're saying that you're running the site at http://localhost and Drupal is still showing http://localhost/drupal/web in the admin?

shadcn avatar Oct 25 '21 15:10 shadcn

No, it shows http://localhost/drupal/web only in the URL aliases screen, the one in the screenshot. The url in the browser is correct, http://localhost.

My Drupal installation is in a subfolder ([web server root]/drupal/web) and then I configure Apache .htaccess files to serve it from http://localhost, using this doc https://www.drupal.org/forum/support/post-installation/2016-06-22/d8-how-to-use-drupal-8-in-a-subfolder (check IntraFusion's comment).

demo-Ghost avatar Oct 25 '21 15:10 demo-Ghost

I faced the same issue (Drupal 9.2). As per @taku3202 comment, applying Drupal Subrequests patches solved it :

KojoEnch avatar Nov 01 '21 14:11 KojoEnch

@demo-Ghost Did you figure this out?

shadcn avatar Nov 20 '21 16:11 shadcn

I'm getting the same error in preview. My Drupal site base URL is http://localhost:8080/blog/drupal/web

EDIT: I've followed exactly the quick start https://next-drupal.org/learn/quick-start path

claudiu-cristea avatar Dec 17 '21 09:12 claudiu-cristea

Also, applying the patches from https://github.com/chapter-three/next-drupal/issues/26#issuecomment-945509653 didn't help

claudiu-cristea avatar Dec 17 '21 09:12 claudiu-cristea

Just for debugging I've replaced the RouterPathTranslatorSubscriber::cleanSubdirInPath() method (see https://github.com/chapter-three/next-drupal/issues/26#issuecomment-950711871) with:

  protected function cleanSubdirInPath($path, Request $request) {
    // Remove any possible leading subdir information in case Drupal is
    // installed under http://example.com/d8/index.php
    if (strpos($path, '/blog/drupal/web/') === 0) {
      $path = substr($path, strlen('/blog/drupal/web/'));
    }
    return $path;
  }

...and it fixes the issue.

Note that I've hardcoded my base path (/blog/drupal/web/) because it seems that I couldn't get it from request. What comes from $request->getBasePath() is an empty string. Same is stored in $GLOBALS['base_path'].

However, we need a permanent fix. I see there is https://www.drupal.org/project/decoupled_router/issues/3133873 but the patch over there is not resolving the issue.

claudiu-cristea avatar Dec 17 '21 10:12 claudiu-cristea

So this works but don't ask my why :) The Symfony request stored in $GLOBALS['request'] contains the correct base path information. But I have no idea why they are different...

  protected function cleanSubdirInPath($path, Request $request) {
    // Remove any possible leading subdir information in case Drupal is
    // installed under http://example.com/d8/index.php
    $regexp = preg_quote($GLOBALS['request']->getBasePath(), '/');
    return preg_replace(sprintf('/^%s/', $regexp), '', $path);
  }

claudiu-cristea avatar Dec 17 '21 14:12 claudiu-cristea

Ok, so I've reworked https://www.drupal.org/project/decoupled_router/issues/3133873. @demo-Ghost could you try to apply also the https://git.drupalcode.org/project/decoupled_router/-/merge_requests/3.diff patch to decoupled_router module and see if fixes the issue? It does for me.

(However, still don't understand why, in cleanSubdirInPath(), $request and $GLOBALS['request'] are diverged)

claudiu-cristea avatar Dec 17 '21 14:12 claudiu-cristea

I'll give it a shot @claudiu-cristea and I'll let you know.

demo-Ghost avatar Dec 22 '21 17:12 demo-Ghost

@claudiu-cristea hi! I had the same issue described here, and your last patch fixed it!

vermario avatar Mar 24 '22 10:03 vermario