{"record":{"id":"6eb9767d810a5c31","repo":"paperclipai/paperclip","slug":"label-must-be-a-positive-finite-number","errorCode":null,"errorMessage":"${label} must be a positive finite number","messagePattern":"(.+?) must be a positive finite number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-state.ts","lineNumber":80,"sourceCode":"export interface PaperclipChatSdkStateOptions extends ChatSdkStateScope {\n  maxValueBytes?: number;\n  now?: () => Date;\n  persistence: ChatSdkStatePersistence;\n}\n\nfunction assertBoundedIdentifier(\n  label: string,\n  value: string,\n  maximum: number,\n): void {\n  if (!value || value.length > maximum) {\n    throw new Error(`${label} must be between 1 and ${maximum} characters`);\n  }\n}\n\nfunction assertTtl(label: string, ttlMs: number): void {\n  if (!(Number.isFinite(ttlMs) && ttlMs > 0)) {\n    throw new Error(`${label} must be a positive finite number`);\n  }\n}\n\nfunction storageKey(kind: StateKind, logicalKey: string): string {\n  assertBoundedIdentifier(\n    \"Chat SDK state key\",\n    logicalKey,\n    MAX_LOGICAL_KEY_LENGTH,\n  );\n  const digest = createHash(\"sha256\").update(logicalKey).digest(\"hex\");\n  return `${kind}:${digest}`;\n}\n\nfunction decodeEnvelope(\n  record: ChatSdkStateRecord,\n  expectedKind: StateKind,\n): unknown {\n  const envelope = record.value as Partial<StateEnvelope> | null;","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-state.ts#L62-L98","documentation":"assertTtl validates that a TTL value is a finite number greater than zero. acquireLock, extendLock, and expiryFromTtl call it; a NaN, Infinity, 0, or negative TTL would produce unusable expiry math, so it is rejected at the boundary.","triggerScenarios":"Calling acquireLock/extendLock with ttlMs = 0, negative values, NaN, or Infinity; computing ttlMs from a Date diff that is <= 0 (already expired) or from a misparsed config string.","commonSituations":"Config parsing where '30' (string) is not converted to a number; Date.now() - startedAt yielding 0 or negative on clock skew; a lock already expired so remaining TTL computes <= 0.","solutions":["Validate ttlMs is a positive finite number before the call: Number.isFinite(ttlMs) && ttlMs > 0","Fix the source computation (e.g. Math.max(minTtl, deadline - Date.now()))","Coerce string config values with Number() and check for NaN"],"exampleFix":"// before\nawait state.acquireLock(key, ttlFromConfig);\n// after\nconst ttl = Number(ttlFromConfig);\nif (!Number.isFinite(ttl) || ttl <= 0) throw new Error(`bad ttl: ${ttlFromConfig}`);\nawait state.acquireLock(key, ttl);","handlingStrategy":"validation","validationCode":"function validTtl(ttlMs: unknown): ttlMs is number { return typeof ttlMs === 'number' && Number.isFinite(ttlMs) && ttlMs > 0; }","typeGuard":"function isPositiveFiniteNumber(v: unknown): v is number { return typeof v === 'number' && Number.isFinite(v) && v > 0; }","tryCatchPattern":"try { await state.acquireLock(key, ttlMs); } catch (err) { if (/must be a positive finite number/.test((err as Error).message)) { ttlMs = DEFAULT_TTL_MS; return state.acquireLock(key, ttlMs); } throw err; }","preventionTips":["Clamp computed TTLs: Math.max(1000, deadline - Date.now())","Coerce config values with Number() and validate on load","Guard against expired-deadline diffs yielding <= 0"],"tags":["validation","ttl","argument-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}