jackwener/OpenCLI · error · CliError

FETCH_ERROR

FETCH_ERROR

Error message

ONES ${apiPath}: ${detail}

What it means

throwIfOnesPeekBusinessError checks filters/peek responses that returned HTTP 200 but whose body is still an error object. If the parsed JSON has a non-empty `reason`, `errcode`, or `type` field (and no `groups` array), the CLI throws FETCH_ERROR with the ONES apiPath and a summarized error detail. This catches ONES ServerError bodies that hide behind a 200 status, typically caused by an invalid filter query.

Source

Thrown at clis/ones/common.js:76

        if (parts.length)
            return parts.filter(Boolean).join(' · ');
    }
    return status === 401 ? 'Unauthorized' : `HTTP ${status}`;
}
/** ONES 部分接口 HTTP 200 但 body 仍为错误(如 reason: ServerError) */
function throwIfOnesPeekBusinessError(apiPath, parsed) {
    if (parsed === null || typeof parsed !== 'object')
        return;
    const o = parsed;
    if (Array.isArray(o.groups))
        return;
    const hasErr = (typeof o.reason === 'string' && o.reason.length > 0) ||
        (typeof o.errcode === 'string' && o.errcode.length > 0) ||
        (typeof o.type === 'string' && o.type.length > 0);
    if (!hasErr)
        return;
    const detail = summarizeOnesError(200, parsed);
    throw new CliError('FETCH_ERROR', `ONES ${apiPath}: ${detail}`, '若 query 不合法会返回 ServerError;可试 opencli ones tasks(空 must)或检查筛选器文档。响应全文可用 -v 或临时打日志。');
}
export async function onesFetchInPageWithMeta(page, apiPath, options = {}) {
    if (!options.skipGoto) {
        await gotoOnesHome(page);
    }
    const url = onesApiUrl(apiPath);
    const method = (options.method ?? 'GET').toUpperCase();
    const auth = options.auth !== false;
    const body = options.body ?? null;
    const includeCt = body !== null || method === 'POST' || method === 'PUT' || method === 'PATCH';
    const headers = buildHeaders(auth, includeCt);
    const urlJs = JSON.stringify(url);
    const methodJs = JSON.stringify(method);
    const headersJs = JSON.stringify(headers);
    const bodyJs = body === null ? 'null' : JSON.stringify(body);
    const raw = await page.evaluate(`
    (async () => {
      const url = ${urlJs};

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Run the simpler command `opencli ones tasks` (empty must clause) to confirm the endpoint and auth work at all.
  2. Re-run with -v (verbose) to dump the full response body and inspect the `reason`/`type` detail.
  3. Validate your filter query: check field uuids and filter syntax against the ONES filters/peek documentation for your deployment version.
  4. Simplify the query: remove optional must clauses and add them back one at a time to find the offending filter.

Example fix

// before: complex peek query
const body = { must: [{ field: 'field009', op: 'in', values: ['unknown-uuid'] }] };
// after: start minimal, then extend
const body = {}; // empty must; add validated filters incrementally
Defensive patterns

Strategy: type-guard

Validate before calling

function peekBodyLooksLikeError(parsed) {
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return false;
  const o = parsed;
  if (Array.isArray(o.groups)) return false;
  return ['reason', 'errcode', 'type'].some(k => typeof o[k] === 'string' && o[k].length > 0);
}
// call only after HTTP ok, and surface parsed.reason before calling flattenPeekGroups

Type guard

function isPeekGroupsResponse(p) {
  return p !== null && typeof p === 'object' && Array.isArray(p.groups);
}

Try / catch

try {
  const rows = await opencli.ones.tasks();
} catch (e) {
  if (e.code === 'FETCH_ERROR' && e.message.includes('filters/peek')) {
    console.error('Peek query rejected:', e.message, '\nHint:', e.hint);
    // fall back to minimal query
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `onesFetchInPage(page, 'project/api/project/filters/peek', ...)` (or a command like tasks/my-tasks that hits filters/peek) where the response is HTTP 200 but the JSON body contains e.g. {reason: 'ServerError'} instead of {groups: [...]}. Happens when the peek query JSON is malformed or references invalid field/filter values.

Common situations: A hand-built filter query with an invalid `must` clause; an ONES server build whose peek endpoint rejects the query shape the CLI sends; a changed/undocumented filter field uuid; copying a query from docs that the deployment's API version does not accept.

Related errors


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