From e2f8a111a508c37d1a60edf5684981631d61f88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20=C3=87orlu?= Date: Sat, 28 Mar 2026 00:15:18 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20unhandled=20promise=20re?= =?UTF-8?q?jection=20in=20Redis=20adapter=20=5Fget=20timeout=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When getTimeoutMilliseconds was configured, _get wrapped the cache read in a new Promise but did not handle rejections from the inner cache.get() call. If Redis rejected the command (e.g. with enableOfflineQueue: false), the rejection went unhandled, which crashes Ghost. Fixed by adding a .catch() that clears the timeout and resolves with null, treating a Redis error during a timed get as a cache miss. --- .../adapters/lib/redis/AdapterCacheRedis.js | 3 +++ .../lib/redis/adapter-cache-redis.test.js | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/ghost/core/core/server/adapters/lib/redis/AdapterCacheRedis.js b/ghost/core/core/server/adapters/lib/redis/AdapterCacheRedis.js index 9a1177aefef..48aee27e5ee 100644 --- a/ghost/core/core/server/adapters/lib/redis/AdapterCacheRedis.js +++ b/ghost/core/core/server/adapters/lib/redis/AdapterCacheRedis.js @@ -181,6 +181,9 @@ class AdapterCacheRedis extends BaseCacheAdapter { this.cache.get(key).then((result) => { clearTimeout(timer); resolve(result); + }).catch(() => { + clearTimeout(timer); + resolve(null); }); }); } diff --git a/ghost/core/test/unit/server/adapters/lib/redis/adapter-cache-redis.test.js b/ghost/core/test/unit/server/adapters/lib/redis/adapter-cache-redis.test.js index 167f7117f4b..c40971efb06 100644 --- a/ghost/core/test/unit/server/adapters/lib/redis/adapter-cache-redis.test.js +++ b/ghost/core/test/unit/server/adapters/lib/redis/adapter-cache-redis.test.js @@ -60,6 +60,24 @@ describe('Adapter Cache Redis', function () { assert.equal(value, 'value from cache'); }); + it('returns null if the cache get rejects and getTimeoutMilliseconds is set', async function () { + const redisCacheInstanceStub = { + get: sinon.stub().rejects(new Error('Stream isn\'t writeable and enableOfflineQueue options is false')), + store: { + getClient: sinon.stub().returns({ + on: sinon.stub() + }) + } + }; + const cache = new RedisCache({ + cache: redisCacheInstanceStub, + getTimeoutMilliseconds: 100 + }); + + const value = await cache.get('key'); + assert.equal(value, null); + }); + it('returns null if getTimeoutMilliseconds is exceeded', async function () { const redisCacheInstanceStub = { get: sinon.stub().callsFake(async () => {