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
- Run the simpler command `opencli ones tasks` (empty must clause) to confirm the endpoint and auth work at all.
- Re-run with -v (verbose) to dump the full response body and inspect the `reason`/`type` detail.
- Validate your filter query: check field uuids and filter syntax against the ONES filters/peek documentation for your deployment version.
- 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
- Start with an empty must clause and add filters incrementally.
- Validate field uuids against your deployment's filter field list before building queries.
- Run with -v during development to capture the full peek response.
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.