{"record":{"id":"8cdd4a978bb9ca6c","repo":"jackwener/OpenCLI","slug":"flomo-api-returned-http-resp-status-please-ref","errorCode":null,"errorMessage":"Flomo API returned HTTP ${resp.status}; please refresh your Flomo login session","messagePattern":"Flomo API returned HTTP (.+?); please refresh your Flomo login session","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/flomo/memos.js","lineNumber":159,"sourceCode":"    updated_at: String(memo.updated_at || ''),\n  };\n}\n\nasync function fetchFlomoJson(url, token) {\n  let resp;\n  try {\n    resp = await fetch(url, {\n      headers: {\n        Authorization: 'Bearer ' + token,\n        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',\n        Accept: 'application/json',\n      },\n    });\n  } catch (err) {\n    throw new CommandExecutionError(`Failed to fetch Flomo memos: ${err instanceof Error ? err.message : String(err)}`);\n  }\n  if (resp.status === 401 || resp.status === 403) {\n    throw new AuthRequiredError(FLOMO_API_DOMAIN, `Flomo API returned HTTP ${resp.status}; please refresh your Flomo login session`);\n  }\n  if (!resp.ok) {\n    throw new CommandExecutionError(`Flomo API returned HTTP ${resp.status}`);\n  }\n  try {\n    return await resp.json();\n  } catch (err) {\n    throw new CommandExecutionError(`Flomo API returned malformed JSON: ${err instanceof Error ? err.message : String(err)}`);\n  }\n}\n\nasync function readAccessToken(page) {\n  const token = unwrapBrowserResult(await page.evaluate(buildGetTokenJs()));\n  if (typeof token !== 'string' || !token.trim()) {\n    throw new AuthRequiredError(FLOMO_API_DOMAIN, 'Flomo memos requires an active signed-in Flomo browser session');\n  }\n  return token.trim();\n}","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/flomo/memos.js#L141-L177","documentation":"fetchFlomoJson treats HTTP 401 or 403 from the Flomo API as an authentication failure and throws AuthRequiredError (a distinct class from CommandExecutionError) with the domain flomoapp.com and the status code in the message. The CLI authenticates with a Bearer token scraped from the signed-in browser session's localStorage (`me.access_token`); a 401/403 means that token is no longer accepted by the API.","triggerScenarios":"Calling `flomo memos` when the browser's stored Flomo access_token has expired or been revoked; user logged out of Flomo in the automation browser; Flomo invalidated old sessions after a password change or security event; token copied from a different/stale profile.","commonSituations":"Long-lived headless browser profile whose Flomo session expired (Flomo tokens have limited lifetime); password reset invalidating all sessions; running the CLI on a machine where the browser was never signed in to Flomo; Flomo server-side session revocation.","solutions":["Open https://v.flomoapp.com in the browser profile used by the CLI and log in / refresh the session.","Confirm you are still signed in to Flomo in that browser (check the `me` localStorage entry for access_token).","Clear Flomo cookies/localStorage for the profile and sign in again to issue a fresh token.","Re-run the CLI immediately after re-login so the freshly scraped token is used."],"exampleFix":"// before: running CLI against a stale browser profile\n$ opencli flomo memos --limit 20\nAuthRequiredError: Flomo API returned HTTP 401; please refresh your Flomo login session\n// after: re-authenticate the browser profile first\n$ opencli auth flomo   # opens browser, sign in, session refreshed\n$ opencli flomo memos --limit 20  # 200 OK","handlingStrategy":"try-catch","validationCode":"// verify a token exists in the browser session before calling the API\nconst token = await readAccessToken(page); // throws AuthRequiredError early if absent\n// optionally probe the session cheaply first; a 401 here means re-login is needed","typeGuard":"null","tryCatchPattern":"import { AuthRequiredError } from '@jackwener/opencli/errors';\ntry {\n  body = await fetchFlomoJson(url, token);\n} catch (err) {\n  if (err instanceof AuthRequiredError) {\n    // token expired/revoked: trigger interactive re-login, then retry once\n    await refreshFlomoSession();\n    body = await fetchFlomoJson(url, await readAccessToken(page));\n  } else { throw err; }\n}","preventionTips":["Catch AuthRequiredError separately from CommandExecutionError to trigger re-authentication flows.","Periodically refresh the Flomo login in the browser profile the CLI uses.","Re-login after password changes — all sessions are invalidated.","Sign in to Flomo in the automation browser profile before first use on a new machine."],"tags":["auth","token-expired","http-401","session","flomo"],"backgroundTag":"jwt-token-expired","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}