ruvnet/ruflo · error

browser/eval: script exceeds maximum length of

Error message

browser/eval: script exceeds maximum length of ${MAX_EVAL_SCRIPT_LENGTH} characters

What it means

Thrown by the browser/eval MCP tool handler when the submitted script exceeds MAX_EVAL_SCRIPT_LENGTH characters. The limit defaults to 20,000 and can be overridden via the CLAUDE_FLOW_MAX_EVAL_SCRIPT_LENGTH environment variable (parsed at module load). The JSON schema also declares maxLength, but this handler-level check is what enforces it deterministically.

Solutions

  1. Split the work into multiple smaller browser/eval calls that each stay under the limit
  2. Move large data out of the script: fetch/derive the data inside the page or via other tools instead of inlining it
  3. Raise the limit deliberately via CLAUDE_FLOW_MAX_EVAL_SCRIPT_LENGTH=<n> on the server process if the workload genuinely needs longer scripts (note: it is read once at startup)

Example fix

// before
await tools.invoke('browser/eval', { script: hugeMinifiedBundle }); // > 20000 chars

// after
for (const chunk of splitScript(hugeMinifiedBundle, 19000)) {
  await tools.invoke('browser/eval', { script: chunk });
}
// or raise the cap for the server process:
// CLAUDE_FLOW_MAX_EVAL_SCRIPT_LENGTH=50000 node server.js
Defensive patterns

Strategy: validation

Validate before calling

const MAX = Number(process.env.CLAUDE_FLOW_MAX_EVAL_SCRIPT_LENGTH) || 20000;
if (script.length > MAX) {
  const chunks = splitScript(script, MAX - 1000);
  for (const c of chunks) await tools.invoke('browser/eval', { script: c });
} else {
  await tools.invoke('browser/eval', { script });
}

Try / catch

try { await tools.invoke('browser/eval', { script }); } catch (e) { if (e instanceof Error && e.message.includes('exceeds maximum length')) { await runInChunks(script); } else throw e; }

Prevention

When it happens

Trigger: Submitting a script longer than 20,000 chars with the default limit; setting CLAUDE_FLOW_MAX_EVAL_SCRIPT_LENGTH lower (e.g. 1000) and then sending previously-accepted scripts; inlining large data blobs or base64 payloads into the script body.

Common situations: Generated scripts that embed scraped page data inline; minified bundles pasted as eval scripts; teams tightening the env limit for security after scripts were already written against the default.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/01f381a7bfcea857. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/browser/src/mcp-tools/browser-tools.ts:671

      properties: {
        session: { type: 'string', description: 'Session ID' },
        script: {
          type: 'string',
          description: `JavaScript code to execute (max ${MAX_EVAL_SCRIPT_LENGTH} chars)`,
          maxLength: MAX_EVAL_SCRIPT_LENGTH,
        },
      },
      required: ['script'],
    },
    handler: async (input) => {
      const script = input.script as string;

      // Validate script length
      if (!script || script.length === 0) {
        throw new Error('browser/eval: script must not be empty');
      }
      if (script.length > MAX_EVAL_SCRIPT_LENGTH) {
        throw new Error(`browser/eval: script exceeds maximum length of ${MAX_EVAL_SCRIPT_LENGTH} characters`);
      }

      // Check for dangerous patterns
      for (const pattern of DANGEROUS_EVAL_PATTERNS) {
        if (pattern.test(script)) {
          throw new Error(`browser/eval: script contains disallowed pattern: ${pattern.source}`);
        }
      }

      // Audit log
      console.info(`[browser/eval] Executing script (${script.length} chars) in session ${input.session || 'default'}`);

      const adapter = getAdapter(input.session as string);
      return adapter.eval({ script });
    },
  },
];

View on GitHub (pinned to fa13ee4ad6)