ComposioHQ/composio · warning · Error

Use only one of before or after.

Error message

Use only one of before or after.

What it means

The beeper-imessage thread listing tools accept before and after cursors for pagination, but they are mutually exclusive — you may pass at most one. Passing both is rejected because the underlying CLI cannot apply two conflicting cursor directions at once.

Source

Thrown at ts/packages/cli-local-tools/src/toolkits/beeper-imessage.ts:158

  return messages[0];
};

const buildGlobalArgs = (input: Record<string, unknown>): string[] => {
  const args: string[] = [];
  const dataDir = optionalString(input.dataDir);
  if (dataDir) args.push('--data-dir', dataDir);
  if (input.useSecondaryInstance === false) args.push('--no-use-secondary-instance');
  // Local tools are one-shot calls. Avoid keeping DB event watchers open unless a
  // future streaming tool explicitly opts in.
  args.push('--no-events');
  if (input.verbose === true) args.push('--verbose');
  return args;
};

const withCursorArgs = (input: Record<string, unknown>, args: string[]): string[] => {
  const before = optionalString(input.before);
  const after = optionalString(input.after);
  if (before && after) throw new Error('Use only one of before or after.');
  if (before) args.push('--before', before);
  if (after) args.push('--after', after);
  return args;
};

const stripAnsi = (text: string): string => text.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '');

const parseImessageCliOutput = (
  commandName: string,
  stdout: string,
  stderr: string
): LocalExecutionResult => {
  const cleanStdout = stripAnsi(stdout);
  const lines = cleanStdout.split(/\r?\n/);
  const okIndex = lines.findIndex(line => /^\[\d+\] ok /.test(line));
  const okLine = okIndex >= 0 ? lines[okIndex] : undefined;
  const match = okLine?.match(/^\[(\d+)\] ok \S+ \((\d+(?:\.\d+)?)ms\)$/);

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Pass only one cursor: before OR after
  2. Clear the opposite field when reusing a params object across pages
  3. Use the returned next-cursor value for the next page rather than tracking both

Example fix

// before
raw({ before: 'cursorA', after: 'cursorB', limit: 50 });
// after
raw({ after: 'cursorB', limit: 50 });
Defensive patterns

Strategy: validation

Validate before calling

const hasBoth = input.before != null && input.after != null;
if (hasBoth) throw new RangeError('pick one cursor');

Type guard

const singleCursor = (i: { before?: string; after?: string }) =>
  (i.before ? 1 : 0) + (i.after ? 1 : 0) <= 1;

Try / catch

try { ... } catch (e) { if (e.message === 'Use only one of before or after.') dropOneCursorAndRetry(); }

Prevention

When it happens

Trigger: Calling page/raw list tools with both input.before and input.after set, e.g. copying a pagination loop that fills both cursors; LLM tool calls that populate every optional field.

Common situations: Auto-generated tool calls from LLMs filling all optional params; refactors that merge two pagination calls into one.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/5d859e33bf86fa50. Report an issue: GitHub.