{"record":{"id":"70f3c9204e8bbe14","repo":"ReactiveX/rxjs","slug":"invalid-time-format-time","errorCode":null,"errorMessage":"Invalid time format: ${time}","messagePattern":"Invalid time format: (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/rxjs/src/testing/scheduled-observable.ts","lineNumber":95,"sourceCode":"    return time;\n  }\n\n  assertTimeFormat(time);\n  const value = parseInt(time.slice(0, -1), 10);\n\n  switch (time.slice(-1)) {\n    case 's':\n      return value * 1000;\n    case 'ms':\n      return value;\n    case 'min':\n      return value * 60 * 1000;\n    case 'hr':\n      return value * 60 * 60 * 1000;\n    case 'd':\n      return value * 24 * 60 * 60 * 1000;\n    default:\n      throw new TypeError(`Invalid time format: ${time}`);\n  }\n}\n\nfunction assertTimeFormat(time: string): asserts time is TimeString {\n  if (!/^\\d+(s|ms|min|hr|d)$/.test(time)) {\n    throw new Error(`Invalid time format: ${time}`);\n  }\n}\n","sourceCodeStart":77,"sourceCodeEnd":104,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/rxjs/src/testing/scheduled-observable.ts#L77-L104","documentation":"Thrown by timeToMilliseconds when a string delay passed to ScheduledObservable.wait() does not end in a unit character the switch statement recognizes. The switch only inspects the LAST character of the string ('s', 'd', etc.), so although assertTimeFormat accepts units like 'min' and 'hr', any string ending in 'n' or 'r' falls through to the default branch and throws a TypeError. This is the deeper of the two format errors because the value can pass the regex assertion and still fail here.","triggerScenarios":"Calling scheduled.wait('5min') or scheduled.wait('2hr') (or any accepted TimeString whose last character is not 's' or 'd'): assertTimeFormat passes, then switch(time.slice(-1)) hits 'n'/'r' and throws `Invalid time format: 5min`. Also note wait('500ms') silently passes but is mis-multiplied as seconds because the switch only sees 's'.","commonSituations":"Migrating RxJS 7 marble/scheduler tests to the new ScheduledObservable test helper and using natural unit strings like '10min' or '1hr' that the TimeString type appears to allow. Passing dynamically built strings or user-supplied config values into wait(). TypeScript template-literal type TimeString suggests 'min'/'hr' are valid, luring developers into the throwing path.","solutions":["Use only units the switch actually handles correctly: plain seconds ('5s'), days ('1d'), or pass a raw millisecond number instead of a string for other units (e.g. wait(5 * 60 * 1000) for 5 minutes).","If you control the call site, convert 'min'/'hr'/'ms' values to milliseconds yourself before calling wait().","Fix the helper: switch on the full unit suffix (extract with a regex capture like /^(\\d+)(ms|s|min|hr|d)$/) so 'min' and 'hr' are handled, eliminating the fall-through TypeError and the 'ms'-treated-as-'s' bug; add tests for every unit."],"exampleFix":"// before\nscheduled.wait('5min'); // throws TypeError: Invalid time format: 5min\n\n// after\nscheduled.wait(5 * 60 * 1000); // milliseconds\n// or, if fixing the library, parse the full suffix:\n// const m = /^(\\d+)(ms|s|min|hr|d)$/.exec(time)!;\n// const mult = { ms: 1, s: 1000, min: 60000, hr: 3600000, d: 86400000 }[m[2]];","handlingStrategy":"validation","validationCode":"import type { TimeString } from './scheduled-observable';\n\n// Only units the current switch handles safely end-to-end\nconst SAFE = /^\\d+(s|d)$/;\nfunction isSafeDelay(v: unknown): v is number | `${number}s` | `${number}d` {\n  return typeof v === 'number' || (typeof v === 'string' && SAFE.test(v));\n}\n\nconst delay: unknown = '5min';\nif (isSafeDelay(delay)) {\n  scheduled.wait(delay);\n} else {\n  scheduled.wait(toMillis(delay as string)); // convert min/hr/ms yourself\n}","typeGuard":"function isSafeTimeString(t: string): t is `${number}s` | `${number}d` {\n  return /^\\d+s$/.test(t) || /^\\d+d$/.test(t);\n}","tryCatchPattern":"try {\n  scheduled.wait(delay as any);\n} catch (e) {\n  if (e instanceof TypeError && /Invalid time format/.test(String(e.message))) {\n    scheduled.wait(toMillisecondsManually(delay)); // fallback: convert to ms number\n  } else {\n    throw e;\n  }\n}","preventionTips":["Prefer passing raw millisecond numbers to wait() to sidestep string parsing entirely.","Avoid 'min'/'hr' strings until the library's unit-switch bug is fixed; they pass the type check but throw at runtime.","Beware 'ms' strings: they currently pass but are scaled as seconds — always verify computed delays in tests."],"tags":["testing","time-format","scheduled-observable","rxjs-next","input-validation"],"backgroundTag":"invalid-time-duration-format","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}