Ghost icon indicating copy to clipboard operation
Ghost copied to clipboard

The presence of a page or post with no content breaks the Content API

Open cathysarisky opened this issue 9 months ago • 8 comments

Issue Summary

Requesting pages or posts from the content API when a one post/page has no 'body' content fails, throwing the error below.

Steps to Reproduce

Create a new post or page in the Ghost editor. Do not put any content in the page. (Do give it a title.) Hit publish.

Attempt to retrieve pages via the content API (probably with limit=all so you're sure to hit it).

Ghost Version

5.108-5.114

Node.js Version

20

How did you install Ghost?

cli (production install)

Database type

MySQL 8

Browser & OS version

any

Relevant log / error output

Error [InternalServerError]: Internal server error, cannot list pages.
      at /home/cathy/algolia index/node_modules/@tryghost/content-api/cjs/content-api.js:271:37
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async TypeCommand.run [as _runHandler] (/home/cathy/algolia index/node_modules/@tryghost/algolia/bin/cli.js:83:29)
      at async Promise.all (index 2) {
    context: 'An unexpected error occurred, please try again. Minified Lexical error #38; visit https://lexical.dev/docs/error?code=38 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.',

^^ although the error above was thrown while trying to index a site, I get the same error by making a GET request with the browser - it doesn't look to be Algolia or SDK-related.

Code of Conduct

  • [x] I agree to be friendly and polite to people in this repository

cathysarisky avatar Mar 23 '25 18:03 cathysarisky

@cathysarisky šŸ‘‹ Hi, I’m a first-time contributor to Ghost and would love to take on this issue. Could you please assign it to me? Thanks!

luoxixiang23921 avatar Apr 07 '25 15:04 luoxixiang23921

@cathysarisky šŸ‘‹ Hi, I’m a first-time contributor to Ghost and would love to take on this issue. Could you please assign it to me? Thanks!

Sorry, I don't have write permissions on the repo. But I can tell you that the core team typically doesn't assign issues to new contributors. [Probably due in part to a lot of drive-by "please assign to me" that never produces code or even a second visit.] So if you want to work on it, feel free to post here that you are, and then file a PR when done! contributing.md is a good place to start for links to how to set up your dev environment, etc.

And welcome to Ghost! :)

cathysarisky avatar Apr 07 '25 16:04 cathysarisky

@cathysarisky Hi! I just set up Ghost locally following the contribution guide and tried to reproduce the issue. I created both a post and a page with only a title (no body content), published them, and then made a request to the Content API. However, the response came back successfully, and everything looked fine on my end. As you can see that the api did respond all the posts including the one I created with just the title "test".

Image

Image

luoxixiang23921 avatar Apr 08 '25 04:04 luoxixiang23921

Hey @luoxixiang23921 , thanks for trying to reproduce! I tried on a local install with the latest (5.116), and couldn't reproduce it. I went back to the production site running 5.114 (where I'd previously seen the problem on 5.108 also), and it was still there. I rolled back a local install to 5.112, and couldn't reproduce. I tried my ghost pro site and couldn't reproduce (which would seem to rule out this being a MySQL bug that doesn't reproduce on local installs).

So... I am guessing the problem is a quirk of my prod site? I'll be interested to see if it persists after my next update.

Closing the issue, since while I can reproduce it on one site, that's not enough for anyone else to bug chase it!

cathysarisky avatar Apr 08 '25 11:04 cathysarisky

@cathysarisky did this ever pop up for you again/eventuallly got resolved? I am having the exact same issue with a customer's site and narrowed it down to 6 (very old) posts, while also trying to index stuff for search.

betschki avatar May 21 '25 10:05 betschki

Hey @betschki , no, I haven't seen it again, although I haven't tried very hard to find it, giving the only place I've seen it is my prod site, where I try to limit my monkeying around.

Possibly useful info about that site: Ghost-CLI install in a Ubuntu VPS. (So it's not uniquely something about your Docker/K8 setup) Fairly recent (~9 months), BUT has content back to like '22, that I imported via an exported json file from my old install. I wondered if it was an old imported/converted mobiledoc issue, but it happens with brand new posts, too.

cathysarisky avatar May 21 '25 11:05 cathysarisky

Interesting. The 6 posts are ranging from 2014 to 2021. The oldest ones were definitely imported at some point, but all of them were mobiledoc. We have tried to re-save them in the Ghost editor, which did convert them to proper (and valid) lexical, but the issue remains.

I'll try to get more information on how the stuff was added into Ghost and will report back here.

betschki avatar May 21 '25 11:05 betschki

Quick update: Creating an entirely new post in my prod setup without post content still causes this error.

{"errors":[{"message":"Internal server error, cannot list posts.","context":"An unexpected error occurred, please try again. Minified Lexical error #38; visit https://lexical.dev/docs/error?code=38 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.","type":"InternalServerError","details":null,"property":null,"help":null,"code":"UNEXPECTED_ERROR","id":"e8590090-363c-11f0-b6b0-c5d04c6a814f","ghostErrorCode":null}]}

That's 5.120, so definitely still live, although maybe only for sites with old imported posts. My oldest posts are definitely from the mobiledoc era.

Fuller error from the server's error log:

{
  "name": "Log",
  "hostname": "srv650399",
  "pid": 582646,
  "level": 50,
  "version": "5.120.0",
  "req": {
    "meta": {
      "requestId": "64af0168-31bf-465c-8ec5-fed47f44614c",
      "userId": null
    },
    "url": "/posts/?key=07362f425d6a52589ad9f7fcb1&limit=all",
    "method": "GET",
    "originalUrl": "/ghost/api/content/posts/?key=07362f425d6a52589ad9f7fcb1&limit=all",
    "params": {},
    "headers": {
      "x-forwarded-for": "*redacted*",
      "x-forwarded-proto": "https",
      "x-real-ip": "*redacted*",
      "host": "*redacted*",
      "connection": "close",
      "cf-ray": "943402a5e91d1752-IAD",
      "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "accept-encoding": "gzip, br",
      "cdn-loop": "cloudflare; loops=1",
      "priority": "u=0, i",
      "accept-language": "en-US,en;q=0.9,ar;q=0.8,de;q=0.7",
      "cf-visitor": "{\"scheme\":\"https\"}",
      "sec-fetch-dest": "document",
      "sec-fetch-user": "?1",
      "cf-connecting-ip": "204.111.114.99",
      "cf-ipcountry": "US",
      "sec-fetch-mode": "navigate",
      "sec-fetch-site": "none",
      "sec-ch-ua": "\"Chromium\";v=\"136\", \"Google Chrome\";v=\"136\", \"Not.A/Brand\";v=\"99\"",
      "sec-ch-ua-mobile": "?0",
      "sec-ch-ua-platform": "\"Windows\"",
      "dnt": "1",
      "upgrade-insecure-requests": "1",
      "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
      "cookie": "**REDACTED**"
    },
    "query": {
      "key": "**REDACTED**",
      "limit": "all"
    }
  },
  "res": {
    "_headers": {
      "x-powered-by": "Express",
      "content-version": "v5.120",
      "vary": "Accept-Version, Accept-Encoding",
      "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
      "access-control-allow-origin": "*",
      "content-type": "application/json; charset=utf-8",
      "content-length": "475",
      "etag": "W/\"1db-BRVni2Zc0H9iFkD0Dj739PgFos8\""
    },
    "statusCode": 500,
    "responseTime": "43ms"
  },
  "err": {
    "id": "8ca6ec20-363d-11f0-b6b0-c5d04c6a814f",
    "domain": "*redacted*",
    "code": "UNEXPECTED_ERROR",
    "name": "InternalServerError",
    "statusCode": 500,
    "level": "critical",
    "message": "An unexpected error occurred, please try again.",
    "context": "\"Minified Lexical error #38; visit https://lexical.dev/docs/error?code=38 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"",
    "stack": "Error: Minified Lexical error #38; visit https://lexical.dev/docs/error?code=38 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\n    at prepareError (/var/www/sws/versions/5.120.0/node_modules/@tryghost/mw-error-handler/lib/mw-error-handler.js:113:19)\n    at n (/var/www/sws/versions/5.120.0/node_modules/lexical/Lexical.prod.js:8:128)\n    at Gf.setEditorState (/var/www/sws/versions/5.120.0/node_modules/lexical/Lexical.prod.js:196:390)\n    at LexicalHTMLRenderer.render (/var/www/sws/versions/5.120.0/node_modules/@tryghost/kg-lexical-html-renderer/build/LexicalHTMLRenderer.js:67:16)\n    at async Object.render (/var/www/sws/versions/5.120.0/core/server/lib/lexical.js:83:16)\n    at async Child.renderIfNeeded (/var/www/sws/versions/5.120.0/core/server/models/post.js:392:26)\n    at async Child.onFetched [as onFetchedCollection] (/var/www/sws/versions/5.120.0/core/server/models/post.js:384:13)",
    "hideStack": false
  },
  "msg": "An unexpected error occurred, please try again.",
  "time": "2025-05-21T12:17:22.915Z",
  "v": 0
}

Other tidbits: The error is not thrown if the blank post is not included in the results. (i.e. by limiting or filtering)

cathysarisky avatar May 21 '25 12:05 cathysarisky

I spent some time digging into this, since it blocks the usage of my search feature for some customers. I found many red herrings on the way, but finally have the culprit earlier today: the collectionsCard lab flag.

Wrote a fix for it and just wanted to add the PR, but @kevinansfield removed the entire flag a few hours ago: https://github.com/TryGhost/Ghost/commit/24edbf496b2bd6bd6f1573b30624963033c53ddb.

My assumption: after this is released it should work again šŸŽ‰

The lab flag caused an empty editor state being passed to the Koenig HTML renderer, which threw the error. This party specifically was responsible, but is now removed:

https://github.com/TryGhost/Ghost/commit/24edbf496b2bd6bd6f1573b30624963033c53ddb#diff-3149f93d170c65cd0c97a204a2d19c6102351b3c542c357aa342874699521237L371-L373

betschki avatar Jun 20 '25 12:06 betschki

Nice debugging!

I definitely had the collectionsCard flag set. In addition to this, it may also have been responsible for the weird behavior I reported in #23356. At least, I hope so, because I'd like it gone. :)

cathysarisky avatar Jun 20 '25 12:06 cathysarisky