koala73/worldmonitor · error · Error

Redis returned an invalid stored value

Error message

Redis returned an invalid stored value

What it means

Thrown by `parseStoredJson` in api/oauth/_refresh-recovery.ts:217 during `rawRedisBeginRefreshAttempt` when the Lua script consumed the refresh token but the stored value under `oauth:refresh:<token>` (returned as `result[1]`) is not a string. The script only returns stored strings from GET, so a non-string means an unexpected RESP type — e.g. false/null leaked through or a key holding a non-string type.

Source

Thrown at api/oauth/_refresh-recovery.ts:217

    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
      'User-Agent': 'worldmonitor-edge/1.0',
    },
    body: JSON.stringify(['EVAL', script, String(keys.length), ...keys, ...args]),
    signal: AbortSignal.timeout(3_000),
  });
  if (!resp.ok) throw new Error(`Redis HTTP ${resp.status}`);
  const data = (await resp.json().catch(() => null)) as { result?: unknown; error?: string } | null;
  if (data?.error) throw new Error(`Redis EVAL failed: ${data.error}`);
  if (!data || !Object.prototype.hasOwnProperty.call(data, 'result')) {
    throw new Error('Redis EVAL returned an invalid response');
  }
  return data.result;
}

function parseStoredJson(value: unknown): unknown {
  if (typeof value !== 'string') throw new Error('Redis returned an invalid stored value');
  try {
    return JSON.parse(value);
  } catch {
    throw new Error('Redis returned malformed JSON');
  }
}

export async function rawRedisBeginRefreshAttempt(
  refreshToken: string,
  attemptId: string,
): Promise<RefreshConsumeResult> {
  const refreshKey = `oauth:refresh:${refreshToken}`;
  const attemptKey = refreshFamilyAttemptKey(refreshToken);
  const pointerKey = refreshFamilyPointerKey(refreshToken);
  const attemptValue = JSON.stringify({ attempt_id: attemptId });
  const attemptMarker = serializeRefreshAttemptMarker(attemptValue);
  const script = [
    "local value = redis.call('GET', KEYS[1])",

View on GitHub (pinned to a96956387a)

Solutions

  1. Inspect `TYPE oauth:refresh:<token>` in Redis; it must be a string holding JSON
  2. Delete or fix the malformed key so the next refresh attempt can repopulate it
  3. Audit for seed scripts or consoles writing these keys outside the Edge code path
Defensive patterns

Strategy: type-guard

Validate before calling

// Before parsing a stored value, confirm it is the expected string type
const raw = await redisGet(`oauth:refresh:${token}`);
if (typeof raw !== 'string') {
  // delete the malformed key / require re-login instead of crashing
}

Type guard

function isStoredString(v: unknown): v is string {
  return typeof v === 'string';
}

Try / catch

try {
  await beginRefreshAttempt(token, id);
} catch (e) {
  if (e instanceof Error && e.message === 'Redis returned an invalid stored value') {
    // quarantine/delete the key; user must re-authenticate
  } else throw e;
}

Prevention

When it happens

Trigger: The consumed `oauth:refresh:<token>` value being absent, boolean false, or a non-string Redis type when the script path returns `{1, value}` with a non-string `value` (e.g. after manual console edits or a format migration gone wrong).

Common situations: Manual Redis console edits writing numbers/other types; a partially applied key-format migration; cross-environment key collisions between dev and prod sharing a database.

Related errors


AI-assisted analysis of koala73/worldmonitor@a96956387a (2026-08-27). Data as JSON: /api/errors/595c7662bc592a22. Report an issue: GitHub.