{"record":{"id":"32861f6e6d7a014c","repo":"jackwener/OpenCLI","slug":"flomo-memos-slug-must-be-an-opaque-memo-cursor-c","errorCode":null,"errorMessage":"flomo memos --slug must be an opaque memo cursor containing only letters, numbers, _ or -","messagePattern":"flomo memos --slug must be an opaque memo cursor containing only letters, numbers, _ or -","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/flomo/memos.js","lineNumber":57,"sourceCode":"  }\n  const text = String(value).trim();\n  if (!/^\\d+$/.test(text)) {\n    throw new ArgumentError('flomo memos --since must be a non-negative Unix timestamp in seconds');\n  }\n  const parsed = Number(text);\n  if (!Number.isSafeInteger(parsed)) {\n    throw new ArgumentError('flomo memos --since must be a safe integer Unix timestamp in seconds');\n  }\n  return parsed;\n}\n\nfunction parseSlugArg(value) {\n  if (value === undefined || value === null || value === '') {\n    return '';\n  }\n  const slug = String(value).trim();\n  if (!/^[A-Za-z0-9_-]{1,256}$/.test(slug)) {\n    throw new ArgumentError('flomo memos --slug must be an opaque memo cursor containing only letters, numbers, _ or -');\n  }\n  return slug;\n}\n\nfunction buildSignedUrl(limit, since, slug) {\n  const params = {\n    limit: String(limit),\n    latest_updated_at: String(since),\n    tz: '8:0',\n    timestamp: String(Math.floor(Date.now() / 1000)),\n    api_key: 'flomo_web',\n    app_version: '4.0',\n    platform: 'web',\n    webp: '1',\n  };\n  if (slug) params.latest_slug = slug;\n  const keys = Object.keys(params).sort();\n  const signBase = keys.map((key) => `${key}=${params[key]}`).join('&');","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/flomo/memos.js#L39-L75","documentation":"Thrown by parseSlugArg in clis/flomo/memos.js:57 when the --slug option (an opaque pagination cursor returned by a previous `flomo memos` call) contains characters outside [A-Za-z0-9_-] or exceeds 256 characters. The library validates cursors defensively because they are embedded in signed request URLs; malformed slugs would produce invalid requests.","triggerScenarios":"`flomo memos --slug \"abc/def\"`, `--slug \"a b\"`, `--slug \"列表\"`, a cursor that was URL-encoded twice (containing %2F), a truncated or hand-edited cursor, or one longer than 256 chars from wrapping/copying errors. Empty or missing slug is fine (returns '').","commonSituations":"Manually editing or truncating a next-page cursor copied from terminal output with line wrapping, storing the cursor in JSON and losing escaping, or passing a different system's slug/ID that contains dots, slashes, or unicode.","solutions":["Use the exact `next` / cursor value returned by the previous `flomo memos` invocation, copied whole without wrapping.","If the cursor was URL-encoded (contains %2B, %3D, etc.), decode it once before passing, or re-capture it from raw output.","Store the cursor in a file to avoid shell quoting corruption: `flomo memos --slug \"$(cat cursor.txt)\"`.","Start a fresh fetch without --slug if the cursor cannot be recovered."],"exampleFix":"// before\nflomo memos --slug \"eyJwYWdlIjoyfQ==/extra\"\n// after\nflomo memos --slug \"eyJwYWdlIjoyfQ\"  # exact cursor from previous response","handlingStrategy":"try-catch","validationCode":"function isValidSlug(s) { return s === undefined || s === '' || /^[A-Za-z0-9_-]{1,256}$/.test(s); }\nif (!isValidSlug(cursor)) throw new Error('cursor corrupted; refetch without --slug');","typeGuard":"function isOpaqueCursor(v) { return typeof v === 'string' && /^[A-Za-z0-9_-]{1,256}$/.test(v); }","tryCatchPattern":"try {\n  page = await runMemos({ slug: cursor });\n} catch (err) {\n  if (err.name === 'ArgumentError' && err.message.includes('--slug')) {\n    console.error('Cursor invalid; restarting pagination from the first page.');\n    page = await runMemos({});\n  } else { throw err; }\n}","preventionTips":["Copy pagination cursors verbatim from the previous command's output; never hand-edit them.","Store cursors in a file or JSON to avoid shell quoting and line-wrap corruption.","Don't URL-encode or decode the cursor before passing it — pass it exactly as returned.","If a cursor fails validation, restart pagination from page 1 rather than repairing it."],"tags":["cli","argument-validation","pagination-cursor"],"backgroundTag":"invalid-cursor","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}