different-ai/openwork · error · RangeError

${name} must be a safe integer greater than or equal to ${mi

Error message

${name} must be a safe integer greater than or equal to ${minimum}.

What it means

validateLimit checks optional ExecutionLimits fields (timeoutMs, etc.) during resolveExecutionLimits. Any provided value must be a safe integer >= the field's minimum (timeoutMs >= 1). Undefined is allowed (falls back to defaults), but anything non-integral, negative, zero where 1 is required, or beyond Number.MAX_SAFE_INTEGER throws a RangeError naming the field and minimum.

Source

Thrown at packages/codemode/src/codemode.ts:127

/** Schema for the structured success or diagnostic returned by CodeMode execution. */
export const Result = Schema.Union([Success, Failure])
/** Result of executing a CodeMode program. Program failures are data, not Effect failures. */
export type Result = typeof Result.Type

/** Reusable confined runtime over one explicit tool tree. */
export type Runtime<R = never> = {
  readonly catalog: () => ReadonlyArray<ToolDescription>
  readonly instructions: () => string
  readonly execute: (code: string) => Effect.Effect<Result, never, R>
}

const validateLimit = <Value extends number | undefined>(
  name: keyof ExecutionLimits,
  value: Value,
  minimum: number,
): Value => {
  if (value !== undefined && (!Number.isSafeInteger(value) || value < minimum)) {
    throw new RangeError(`${name} must be a safe integer greater than or equal to ${minimum}.`)
  }
  return value
}

const resolveExecutionLimits = (limits?: ExecutionLimits): ResolvedExecutionLimits => ({
  timeoutMs: validateLimit("timeoutMs", limits?.timeoutMs, 1),
  maxToolCalls: validateLimit("maxToolCalls", limits?.maxToolCalls, 0),
  maxOutputBytes: validateLimit("maxOutputBytes", limits?.maxOutputBytes, 0),
})

/** Executes one Effect-native CodeMode program without constructing a reusable runtime. */
export const execute = <const Tools extends Record<string, unknown>>(
  options: ExecuteOptions<Tools>,
): Effect.Effect<Result, never, Services<Tools>> => {
  const tools = (options.tools ?? {}) as HostTools<Services<Tools>>
  ToolRuntime.assertValidTools(tools)
  return executeWithLimits(options, resolveExecutionLimits(options.limits), ToolRuntime.searchIndex(tools))
}

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Coerce config values with Number() and ensure integer >= 1 before passing (e.g. Math.max(1, Math.floor(ms)))
  2. Omit the field (undefined) to use library defaults instead of passing 0
  3. Validate env/JSON-derived values in your config loader with Number.isSafeInteger

Example fix

// before
resolveExecutionLimits({ timeoutMs: process.env.TIMEOUT_MS }) // string
// after
const t = Number(process.env.TIMEOUT_MS)
resolveExecutionLimits({ timeoutMs: Number.isSafeInteger(t) && t >= 1 ? t : undefined })
Defensive patterns

Strategy: validation

Validate before calling

function parseLimit(v: unknown, min = 1): number | undefined {
  const n = typeof v === "string" ? Number(v) : v
  if (n === undefined || n === null) return undefined
  if (typeof n !== "number" || !Number.isSafeInteger(n) || n < min) {
    throw new Error(`Limit must be a safe integer >= ${min}`)
  }
  return n
}
resolveExecutionLimits({ timeoutMs: parseLimit(raw.timeoutMs) })

Type guard

const isSafeLimit = (v: unknown): v is number =>
  typeof v === "number" && Number.isSafeInteger(v) && v >= 1

Try / catch

try {
  limits = resolveExecutionLimits(userLimits)
} catch (e) {
  if (e instanceof RangeError && e.message.includes("safe integer")) {
    limits = resolveExecutionLimits(undefined) // fall back to defaults
  } else throw e
}

Prevention

When it happens

Trigger: Passing limits like { timeoutMs: 0 }, { timeoutMs: 1.5 }, { timeoutMs: NaN }, { timeoutMs: "5000" } (string), or a value < the field minimum into resolveExecutionLimits / the codemode execution options.

Common situations: Loading limits from env vars or JSON config without Number coercion; computing a timeout with arithmetic that yields NaN; setting 0 intending 'unlimited' when 1 is the minimum.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/ce31945565e72318. Report an issue: GitHub.