{"record":{"id":"e588b4c45b3d4116","repo":"ruvnet/ruflo","slug":"invalid-policy-rule-limit","errorCode":null,"errorMessage":"invalid-policy-rule-limit","messagePattern":"invalid-policy-rule-limit","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/security/src/policy/engine.ts","lineNumber":86,"sourceCode":"  setMode(mode: PolicyState['mode']): void {\n    this.state.mode = mode;\n  }\n\n  setConfiguredMode(mode: PolicyState['mode']): void {\n    this.state.configuredMode = mode;\n    const rank = { legacy: 0, observe: 1, enforce: 2 } as const;\n    if (rank[mode] > rank[this.state.mode]) this.state.mode = mode;\n  }\n\n  upsertRule(rule: PolicyRule): void {\n    if (!rule.id || !rule.actions.length) throw new Error('invalid-policy-rule');\n    for (const value of [\n      rule.constraints?.maxCostUsd,\n      rule.constraints?.maxTokens,\n      rule.constraints?.maxConcurrency,\n    ]) {\n      if (value !== undefined && (!Number.isFinite(value) || value < 0)) {\n        throw new Error('invalid-policy-rule-limit');\n      }\n    }\n    const index = this.state.rules.findIndex((item) => item.id === rule.id);\n    if (index >= 0) this.state.rules[index] = structuredClone(rule);\n    else this.state.rules.push(structuredClone(rule));\n  }\n\n  setBudget(limit: BudgetLimit): void {\n    if (limit.periodMs <= 0) throw new Error('invalid-budget-period');\n    if (!Number.isFinite(limit.periodMs)\n      || [limit.maxCostUsd, limit.maxTokens].some((value) => (\n        value !== undefined && (!Number.isFinite(value) || value < 0)\n      ))) throw new Error('invalid-budget-limit');\n    const index = this.state.budgets.findIndex((item) => item.id === limit.id);\n    if (index >= 0) this.state.budgets[index] = structuredClone(limit);\n    else this.state.budgets.push(structuredClone(limit));\n  }\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/security/src/policy/engine.ts#L68-L104","documentation":"When a rule defines optional numeric constraints (maxCostUsd, maxTokens, maxConcurrency), upsertRule() checks each: a value that is present but non-finite or negative throws Error('invalid-policy-rule-limit'). Limits are caps, so NaN, Infinity, and negative numbers are all meaningless and rejected.","triggerScenarios":"constraints: { maxCostUsd: -1 } from a sign typo; { maxTokens: NaN } from parsing 'unlimited' or an empty env var; { maxConcurrency: Infinity } used to mean 'no cap'.","commonSituations":"Env-driven config where an unset variable parses to NaN; using -1 as a 'disabled' sentinel (common in other systems) which this engine rejects; spreadsheet-edited policy files with negative values.","solutions":["Use undefined (omit the key) to express 'no limit' — the check only applies when the value is defined.","Guard numeric env parsing: only assign when Number.isFinite(v) && v >= 0.","Correct negative values to zero or a positive cap depending on intent."],"exampleFix":"// before\nconst maxTokens = Number(env.MAX_TOKENS); // NaN when unset -> throws\nengine.upsertRule({ id: 'r', actions: ['run'], constraints: { maxTokens } });\n\n// after\nconst parsed = Number(env.MAX_TOKENS);\nconst maxTokens = Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;\nengine.upsertRule({ id: 'r', actions: ['run'], constraints: { maxTokens } });","handlingStrategy":"validation","validationCode":"function sanitizeLimit(v: number | undefined): number | undefined {\n  if (v === undefined) return undefined;\n  if (!Number.isFinite(v) || v < 0) throw new Error(`invalid limit: ${v}`);\n  return v;\n}\nengine.upsertRule({\n  id: 'r',\n  actions: ['run'],\n  constraints: {\n    maxCostUsd: sanitizeLimit(parsedCost),\n    maxTokens: sanitizeLimit(parsedTokens),\n    maxConcurrency: sanitizeLimit(parsedConcurrency),\n  },\n});","typeGuard":"function isValidLimit(v: unknown): v is number {\n  return v === undefined || (typeof v === 'number' && Number.isFinite(v) && v >= 0);\n}","tryCatchPattern":"try {\n  engine.upsertRule(rule);\n} catch (err) {\n  if (err instanceof Error && err.message === 'invalid-policy-rule-limit') {\n    throw new ConfigError(`rule ${rule.id} has a non-finite or negative constraint`);\n  }\n  throw err;\n}","preventionTips":["Express 'unlimited' by omitting the constraint key, never with -1 or Infinity.","Centralize env->number parsing in one helper that returns undefined for non-finite results.","Unit-test config parsing with unset, empty, and negative values."],"tags":["policy","numbers","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}