jackwener/OpenCLI · error · AuthRequiredError
${message}
Error message
${message} What it means
When the parsed Flomo envelope has body.code !== 0, the command in clis/flomo/memos.js:207 builds a message from body.message (or a fallback with the code). If isAuthFailureMessage() detects auth-related keywords (auth, login, token, permission, 登录, etc.), it throws AuthRequiredError; otherwise the server's own message is rethrown as a CommandExecutionError. This propagates Flomo's application-level business/API errors to the caller.
Source
Thrown at clis/flomo/memos.js:207
{ name: 'limit', type: 'int', default: 20, help: 'Number of memos to fetch (1-200)' },
{ name: 'since', type: 'int', help: 'Only memos updated after this Unix timestamp in seconds' },
{ name: 'slug', help: 'Pagination cursor from a previous memo page' },
],
columns: ['id', 'url', 'content', 'slug', 'tags', 'images', 'created_at', 'updated_at'],
func: async (page, kwargs) => {
const limit = parsePositiveIntArg(kwargs.limit, 'limit', 20, MAX_LIMIT);
const since = parseSinceArg(kwargs.since);
const slug = parseSlugArg(kwargs.slug);
await page.wait(3).catch(() => {});
const token = await readAccessToken(page);
const body = await fetchFlomoJson(buildSignedUrl(limit, since, slug), token);
if (!body || typeof body !== 'object' || Array.isArray(body)) {
throw new CommandExecutionError('Flomo API returned a malformed response');
}
if (body.code !== 0) {
const message = body.message || `Flomo API error code ${body.code}`;
if (isAuthFailureMessage(message)) {
throw new AuthRequiredError(FLOMO_API_DOMAIN, message);
}
throw new CommandExecutionError(message);
}
if (!Array.isArray(body.data)) {
throw new CommandExecutionError('Flomo API returned malformed memo data');
}
if (body.data.length === 0) {
throw new EmptyResultError('flomo memos', 'No Flomo memos matched the requested filters.');
}
return body.data.map(normalizeMemo);
},
});
export const __test__ = {
buildSignedUrl,
command,
normalizeMemo,
parsePositiveIntArg,View on GitHub (pinned to 49907e53dc)
Solutions
- Read the surfaced message — it comes from Flomo's body.message and states the server-side reason directly
- If rate-limited, back off and retry after a delay; reduce polling frequency and lower --limit
- If the error mentions sign/params, verify the app hasn't changed the signing scheme or required query params and update buildSignedUrl accordingly
- Check Flomo service status / try the web app to determine whether it is a transient server-side incident
Example fix
// before (tight retry loop that keeps hitting server error codes)
for (;;) { await runMemosCommand(); }
// after (respect the server message, back off and retry with a cap)
try {
await runMemosCommand();
} catch (err) {
if (err instanceof CommandExecutionError && /rate|limit|freq/i.test(err.message)) {
await new Promise((r) => setTimeout(r, 30_000));
} else {
throw err;
}
} Defensive patterns
Strategy: retry
Validate before calling
// Validate args the server also enforces, before calling the API
const limit = Number(kwargs.limit ?? 20);
if (!Number.isInteger(limit) || limit < 1 || limit > 200) {
throw new Error('limit must be an integer between 1 and 200');
}
if (kwargs.slug && !/^[A-Za-z0-9_-]{1,256}$/.test(kwargs.slug)) {
throw new Error('slug must be a valid memo cursor (letters, digits, _, -)');
} Type guard
function isApiErrorEnvelope(body) {
return (
body !== null && typeof body === 'object' && !Array.isArray(body) &&
typeof body.code === 'number' && body.code !== 0
);
} Try / catch
try {
await runFlomoMemos();
} catch (err) {
if (err instanceof CommandExecutionError && /rate|limit|freq|too many/i.test(err.message)) {
await sleep(30_000); // back off on rate-limit style server errors
return runFlomoMemos();
}
if (err instanceof AuthRequiredError) {
await refreshFlomoLogin();
return runFlomoMemos();
}
throw err;
} Prevention
- Throttle polling frequency and keep --limit within documented bounds
- Only pass slug cursors exactly as returned by a previous successful page
- Back off exponentially when server-side error codes appear
- Monitor Flomo status for incidents; keep the CLI's sign/params up to date
When it happens
Trigger: Flomo responds with a JSON envelope whose code is nonzero — e.g. rate limiting, invalid sign/parameters, account restrictions, or server-side business errors — with a message containing no auth-related keywords.
Common situations: Exceeding Flomo API rate limits with frequent polling; sending an invalid slug pagination cursor or out-of-range limit accepted locally but rejected server-side; Flomo service incidents returning error codes; Flomo changing sign algorithm or required params (api_key/app_version) causing server rejection.
Related errors
- TikTok Studio item_list failed: ${statusMsg || statusCode}
- API_ERROR
- API_ERROR
- coingecko derivatives returned HTTP 429 (rate limited)
- coingecko derivatives returned HTTP ${resp.status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/3f4d72e6fc9a40ae.
Report an issue: GitHub.