{"record":{"id":"a94862e9323958bd","repo":"ruvnet/ruflo","slug":"invalid-budget-period","errorCode":null,"errorMessage":"invalid-budget-period","messagePattern":"invalid-budget-period","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/security/src/policy/engine.ts","lineNumber":95,"sourceCode":"\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\n  issueApproval(approval: Omit<PolicyApproval, 'uses' | 'issuedAt'> & { uses?: number; issuedAt?: number }): PolicyApproval {\n    if (approval.issuedBy === approval.principal) throw new Error('self-approval-forbidden');\n    if (this.approvalIssuerVerifier?.(approval.issuedBy) !== true) {\n      throw new Error('untrusted-approval-issuer');\n    }\n    const issuedAt = approval.issuedAt ?? this.now();\n    const record: PolicyApproval = { ...approval, issuedAt, uses: approval.uses ?? 0 };\n    if (this.state.approvals.some((item) => item.id === record.id)) throw new Error('duplicate-approval-id');\n    if (!record.id","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/security/src/policy/engine.ts#L77-L113","documentation":"PolicyEngine.setBudget() rejects a BudgetLimit whose periodMs is <= 0 with Error('invalid-budget-period'). A budget window of zero or negative duration can never accrue, so it is treated as a programming error rather than an empty budget.","triggerScenarios":"setBudget({ id: 'b', periodMs: 0 }) from a default placeholder; periodMs computed as end - start where the timestamps are equal or reversed (negative result); passing seconds (e.g. 60) where a multi-hour window was expected still works, but 0 or negatives throw.","commonSituations":"Computing the window from two Date.now() calls in quick succession; config files that omit the period key and default it to 0; unit tests constructing budgets with dummy values.","solutions":["Set a positive period in milliseconds, e.g. 3_600_000 for one hour.","When deriving periodMs from timestamps, validate start < end before constructing the limit.","Reject budget config at load time if periodMs is missing or non-positive."],"exampleFix":"// before\nengine.setBudget({ id: 'daily', periodMs: 0, maxCostUsd: 5 });\n\n// after\nengine.setBudget({ id: 'daily', periodMs: 24 * 3_600_000, maxCostUsd: 5 });","handlingStrategy":"validation","validationCode":"if (!Number.isFinite(budget.periodMs) || budget.periodMs <= 0) {\n  throw new Error(`budget ${budget.id}: periodMs must be > 0 (got ${budget.periodMs})`);\n}\nengine.setBudget(budget);","typeGuard":"function hasPositivePeriod(b: BudgetLimit): boolean {\n  return Number.isFinite(b.periodMs) && b.periodMs > 0;\n}","tryCatchPattern":"try {\n  engine.setBudget(budget);\n} catch (err) {\n  if (err instanceof Error && err.message === 'invalid-budget-period') {\n    return configError(`budget '${budget.id}' needs a positive periodMs`);\n  }\n  throw err;\n}","preventionTips":["Define budgets in human units (hours/days) and multiply to milliseconds at one call site.","When computing periodMs from timestamps, assert end > start first.","Never use 0 as a placeholder for required duration fields."],"tags":["policy","budget","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"}