jackwener/OpenCLI · error · CommandExecutionError

Flomo API returned malformed memo data

Error message

Flomo API returned malformed memo data

What it means

clis/flomo/memos.js throws this CommandExecutionError after a Flomo API response arrives but its `data` field is not an array. The memos command expects the envelope `{ data: [...] }`; any other shape means the API contract changed or the response is not memo data. It guards downstream `body.data.map(normalizeMemo)` from crashing on a non-array.

Source

Thrown at clis/flomo/memos.js:212

  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,
  parseSinceArg,
  parseSlugArg,
};

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command with a valid, fresh Flomo session/auth to rule out a redirected login response
  2. Inspect the raw response body (log `body` before validation) to see what shape actually arrived
  3. Update opencli/flomo CLI to a version matching the current Flomo API response format
  4. If Flomo changed its schema, patch the parsing code so `body.data` matches the new envelope

Example fix

// before
if (!Array.isArray(body.data)) {
  throw new CommandExecutionError('Flomo API returned malformed memo data');
}
// after
const data = Array.isArray(body?.data) ? body.data : Array.isArray(body) ? body : null;
if (!data) {
  throw new CommandExecutionError(`Flomo API returned malformed memo data: ${JSON.stringify(body).slice(0, 200)}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!apiResponse || !Array.isArray(apiResponse.data)) {
  throw new Error('Flomo response missing data array; aborting before map()');
}

Type guard

function hasMemoData(body) {
  return typeof body === 'object' && body !== null && Array.isArray(body.data);
}

Try / catch

try {
  const memos = await flomoMemos(filters);
} catch (err) {
  if (err.message.includes('malformed memo data')) {
    logRawResponseForDebugging(); // inspect actual body shape / re-auth
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The Flomo API returns 200 with a body where `data` is missing, an object, a string, or null — e.g. a changed response schema, an HTML/JSON error page parsed into `{}`, or an unexpected payload from the memos endpoint.

Common situations: Flomo changes its private/API response shape; a proxy or captive portal returns HTML; the request hits a login page instead of memo JSON; stale session produces an empty object body that passes auth checks.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/1ad7745a435772d6. Report an issue: GitHub.