Unable to convert from source HTML to Mobiledoc: The html-to-mobiledoc package was not installed

  • What’s your URL? https://grantwinney.com
  • What version of Ghost are you using? 2.19.0
  • What configuration? self-hosted
  • What browser? Brave 0.61.52 Chromium: 73.0.3683.86 (Official Build) (64-bit)
  • What errors or information do you see in the console? n/a
  • What steps could someone else take to reproduce the issue you’re having? details below

I’m using the Admin API to create a new post using Html instead of MobileDoc format. I’ve verified the endpoint works just fine if I post MobileDoc instead of HTML, but when I try to post HTML by specifying only a few fields like this…

/posts/?source=html

title = "This is a test post using html instead of mobiledoc"
html = "<h1>Test Header</h1><b>Test Body</b>"
status = "draft"

I get a response back like this:

{
	"errors": [{
		"message": "Internal server error, cannot save post.",
		"context": "Unable to convert from source HTML to Mobiledoc The html-to-mobiledoc package was not installed",
		"type": "InternalServerError",
		"details": null,
		"property": null,
		"help": "Please review any errors from the install process by checking the Ghost logs",
		"code": "HTML_TO_MOBILEDOC_INSTALLATION",
		"id": "f31c8c20-509e-11e9-bdcc-49c5abbe537b"
	}]
}

From the production log:

{
	"name": "Log",
	"hostname": "ghost-blog",
	"pid": 1621,
	"level": 50,
	"req": {
		"meta": {
			"requestId": "6e6999a0-509e-11e9-bdcc-49c5abbe537b",
			"userId": null
		},
		"url": "/posts/?source=html",
		"method": "POST",
		"originalUrl": "/ghost/api/v2/admin/posts/?source=html",
		"params": {},
		"headers": {
			"x-forwarded-for": "192.182.255.2",
			"x-forwarded-proto": "https",
			"x-real-ip": "192.182.255.2",
			"host": "grantwinney.com",
			"connection": "close",
			"content-length": "138",
			"accept": "application/json, text/json, text/x-json, text/javascript, application/xml, text/xml",
			"user-agent": "RestSharp/106.6.9.0",
			"accept-encoding": "gzip, deflate",
			"content-type": "application/json"
		},
		"body": {
			"posts": {
				"0": {
					"title": "This is a test post using html instead of mobiledoc",
					"html": "<h1>Test Header</h1><b>Test Body</b>",
					"status": "draft"
				}
			}
		},
		"query": {
			"source": "html"
		}
	},
	"res": {
		"_headers": {
			"x-powered-by": "Express",
			"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
			"content-type": "application/json; charset=utf-8",
			"content-length": "405",
			"etag": "W/\"195-AZskq4XiyOphICUxoXz9th4TvDw\"",
			"vary": "Accept-Encoding"
		},
		"statusCode": 500,
		"responseTime": "149ms"
	},
	"err": {
		"id": "6e7f6b90-509e-11e9-bdcc-49c5abbe537b",
		"domain": "https://grantwinney.com",
		"code": "HTML_TO_MOBILEDOC_INSTALLATION",
		"name": "InternalServerError",
		"statusCode": 500,
		"level": "critical",
		"message": "Unable to convert from source HTML to Mobiledoc",
		"context": "\"The html-to-mobiledoc package was not installed\"",
		"help": "\"Please review any errors from the install process by checking the Ghost logs\"",
		"stack": "InternalServerError: Unable to convert from source HTML to Mobiledoc\n    at new InternalServerError (/var/www/ghost/versions/2.19.0/node_modules/ghost-ignition/lib/errors/index.js:77:23)\n    at Object.<anonymous> (/var/www/ghost/versions/2.19.0/core/server/lib/mobiledoc/converters/index.js:29:27)\n    at Object.add (/var/www/ghost/versions/2.19.0/core/server/api/v2/utils/serializers/input/posts.js:153:75)\n    at serializeOptionsShared (/var/www/ghost/versions/2.19.0/core/server/api/shared/serializers/handle.js:55:75)\n    at /var/www/ghost/versions/2.19.0/core/server/lib/promise/sequence.js:10:31\n    at tryCatcher (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/util.js:16:23)\n    at Object.gotValue (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/reduce.js:157:18)\n    at Object.gotAccum (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/reduce.js:144:25)\n    at Object.tryCatcher (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/util.js:16:23)\n    at Promise._settlePromiseFromHandler (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:512:31)\n    at Promise._settlePromise (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:569:18)\n    at Promise._settlePromise0 (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:614:10)\n    at Promise._settlePromises (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:694:18)\n    at _drainQueueStep (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:138:12)\n    at _drainQueue (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:131:9)\n    at Async._drainQueues (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:147:5)\n    at Immediate.Async.drainQueues (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:17:14)\n\nError: Cannot find module 'jsdom'\n    at Function.Module._resolveFilename (module.js:476:15)\n    at Function.Module._load (module.js:424:25)\n    at Module.require (module.js:504:17)\n    at require (internal/module.js:20:19)\n    at Object.<anonymous> (/var/www/ghost/versions/2.19.0/node_modules/@tryghost/html-to-mobiledoc/lib/converter.js:5:17)\n    at Module._compile (module.js:577:32)\n    at Object.Module._extensions..js (module.js:586:10)\n    at Module.load (module.js:494:32)\n    at tryModuleLoad (module.js:453:12)\n    at Function.Module._load (module.js:445:3)\n    at Module.require (module.js:504:17)\n    at require (internal/module.js:20:19)\n    at Object.<anonymous> (/var/www/ghost/versions/2.19.0/node_modules/@tryghost/html-to-mobiledoc/index.js:1:80)\n    at Module._compile (module.js:577:32)\n    at Object.Module._extensions..js (module.js:586:10)\n    at Module.load (module.js:494:32)"
	},
	"msg": "Unable to convert from source HTML to Mobiledoc",
	"time": "2019-03-27T14:41:40.055Z",
	"v": 0
}

Here’s the stack from the logs more nicely formatted:

InternalServerError: Unable to convert from source HTML to Mobiledoc
    at new InternalServerError (/var/www/ghost/versions/2.19.0/node_modules/ghost-ignition/lib/errors/index.js:77:23)
    at Object.<anonymous> (/var/www/ghost/versions/2.19.0/core/server/lib/mobiledoc/converters/index.js:29:27)
    at Object.add (/var/www/ghost/versions/2.19.0/core/server/api/v2/utils/serializers/input/posts.js:153:75)
    at serializeOptionsShared (/var/www/ghost/versions/2.19.0/core/server/api/shared/serializers/handle.js:55:75)
    at /var/www/ghost/versions/2.19.0/core/server/lib/promise/sequence.js:10:31
    at tryCatcher (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/util.js:16:23)
    at Object.gotValue (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/reduce.js:157:18)
    at Object.gotAccum (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/reduce.js:144:25)
    at Object.tryCatcher (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/promise.js:694:18)
    at _drainQueueStep (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:138:12)
    at _drainQueue (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:131:9)
    at Async._drainQueues (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:147:5)
    at Immediate.Async.drainQueues (/var/www/ghost/versions/2.19.0/node_modules/bluebird/js/release/async.js:17:14)

Error: Cannot find module 'jsdom'
    at Function.Module._resolveFilename (module.js:476:15)
    at Function.Module._load (module.js:424:25)
    at Module.require (module.js:504:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/var/www/ghost/versions/2.19.0/node_modules/@tryghost/html-to-mobiledoc/lib/converter.js:5:17)
    at Module._compile (module.js:577:32)
    at Object.Module._extensions..js (module.js:586:10)
    at Module.load (module.js:494:32)
    at tryModuleLoad (module.js:453:12)
    at Function.Module._load (module.js:445:3)
    at Module.require (module.js:504:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/var/www/ghost/versions/2.19.0/node_modules/@tryghost/html-to-mobiledoc/index.js:1:80)
    at Module._compile (module.js:577:32)
    at Object.Module._extensions..js (module.js:586:10)
    at Module.load (module.js:494:32)

Guessing you’re running on Node v6 which doesn’t support html -> mobiledoc.

When I run node -v I get v6.16.0.

Ran this and rebooted the server:

sudo npm install -g npm
sudo npm install -g n
sudo n stable

Now I’m on v11.12.0 but I tried the same POST again and got the same error.

Do I have to install the html-to-mobiledoc package separately?

After switching Node versions, you would need to reinstall all dependencies. The easiest way is to delete the node_modules folder and then run npm/yarn again.

Or if you’re using ghost-cli use ghost upgrade --force.

Keep in mind though, that v11 is an unstable, unsupported version of Node. Ghost supports LTS only: Supported node versions for self-hosted installs of Ghost

It is unfortunate that we weren’t able to make html-to-mobiledoc work on Node v6. However, jsdom has already dropped support for Node v6, and as it’s a new feature and only a few weeks before Node v6 is completely EOL, we chose to ship the feature with support in Node v8 and v10 only.

1 Like

Thanks so much @Hannah! I downgraded Node and forced the upgrade, and it’s working as expected now. :+1:

sudo n 10.15.3
ghost upgrade --force

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.