{"record":{"id":"0958129e5a0f632b","repo":"mui/material-ui","slug":"tries-limit-reached","errorCode":null,"errorMessage":"tries limit reached","messagePattern":"tries limit reached","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages-internal/waterfall/waitUntil.mjs","lineNumber":12,"sourceCode":"import sleep from './sleep.mjs';\n\nexport default async function waitUntil(test, options = {}) {\n  const { delay = 5e3, tries = -1 } = options;\n  const { predicate, result } = await test();\n\n  if (predicate) {\n    return result;\n  }\n\n  if (tries - 1 === 0) {\n    throw new Error('tries limit reached');\n  }\n\n  await sleep(delay);\n  return waitUntil(test, { ...options, tries: tries > 0 ? tries - 1 : tries });\n}\n","sourceCodeStart":1,"sourceCodeEnd":18,"githubUrl":"https://github.com/mui/material-ui/blob/bdc96df2cb530fcdd60a7a7aabe37f610ce0f0a5/packages-internal/waterfall/waitUntil.mjs#L1-L18","documentation":"waitUntil is a tiny polling helper: it calls test(), returns its result when predicate is true, otherwise sleeps `delay` ms and recurses, decrementing `tries`. When the configured try budget is exhausted (tries-1 === 0) it throws 'tries limit reached' with no diagnostic about what condition failed. Default tries is -1 (unlimited), so this only fires when a caller explicitly passes a finite tries.","triggerScenarios":"Calling waitUntil(test, { tries: N }) where test() never returns predicate:true within N attempts; used by the waterfall/deploy scripts to wait on a deploy URL, service health, or build artifact that never becomes available.","commonSituations":"Deploy/preview URL polling where the deploy failed; waiting on a service that never comes up; tries set too low for slow CI environments; the predicate function has a bug and always returns false.","solutions":["Inspect what waitUntil is polling (the test function) and verify that target actually reaches the expected state manually.","Increase the tries option (or pass tries:-1 for unlimited during interactive debugging) if the target is just slow.","Increase delay if you are hitting rate limits on the polled endpoint.","Fix the predicate logic if it never returns true due to a comparison bug."],"exampleFix":"// before\nawait waitUntil(async () => ({ predicate: urlUp(deployUrl) }), { tries: 3, delay: 5000 });\n// after — debug the predicate, then raise budget\nawait waitUntil(async () => {\n  const ok = await urlUp(deployUrl);\n  console.log('urlUp?', ok);\n  return { predicate: ok };\n}, { tries: 30, delay: 5000 });","handlingStrategy":"retry","validationCode":"// Validate the predicate synchronously where possible before polling.\nasync function probeOnce(test) { return (await test()).predicate; }\n// if (await probeOnce(test)) is false and tries is small, raise the budget","typeGuard":null,"tryCatchPattern":"try {\n  await waitUntil(test, { tries: 30, delay: 5000 });\n} catch (e) {\n  if (e.message === 'tries limit reached') {\n    // log diagnostic and either rethrow or extend the budget\n    throw new Error(`waitUntil gave up: condition never became true (${test.toString()})`);\n  }\n  throw e;\n}","preventionTips":["Set tries large enough for the slowest expected target (slow CI, cold deploys).","Log inside the predicate so you can see what state it observed when it fails.","For interactive runs, default to unlimited tries (tries:-1) and cancel manually."],"tags":["runtime","polling","ci","deploy","waterfall"],"backgroundTag":null,"analyzedSha":"bdc96df2cb530fcdd60a7a7aabe37f610ce0f0a5","analyzedAt":"2026-08-12T22:59:56.717Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}