{"record":{"id":"77c9201edf641bad","repo":"decolua/9router","slug":"autoping-provider-connection-id-ping-fail","errorCode":null,"errorMessage":"[AutoPing] ${provider}:${connection.id}: ping failed (reset ${resetAt})","messagePattern":"\\[AutoPing\\] (.+?):(.+?): ping failed \\(reset (.+?)\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/shared/services/quotaAutoPing.js","lineNumber":235,"sourceCode":"  state.resetCache[key] = resetAt;\n\n  if (providerConfig.skipWhenBlockingQuotaExhausted && hasExhaustedBlockingQuota(quotas, providerConfig.quotaKey)) return;\n  if (isQuotaExhausted(quota)) return;\n\n  const now = Date.now();\n  const resetKey = normalizeResetKey(resetAt);\n  const lastPingedResetKey = connection.lastPingedResetKey || normalizeResetKey(connection.lastPingedResetAt);\n\n  // Claude waits for reset. Codex pings only when resetAt slides, which means the 5h window is inactive.\n  if (!shouldPingForReset(providerConfig, cachedReset, resetAt, now)) return;\n  if (wasPingedRecently(connection, providerConfig.minPingIntervalMs, now)) return;\n  if (lastPingedResetKey === resetKey) return;\n\n  const ok = await handler.sendPing(connection, providerConfig, proxyOptions, deps);\n  if (!ok) {\n    // Do not mark reset as pinged unless upstream accepted the tiny request.\n    state.failureCache[key] = Date.now();\n    console.warn(`[AutoPing] ${provider}:${connection.id}: ping failed (reset ${resetAt})`);\n    return;\n  }\n\n  delete state.failureCache[key];\n  await deps.updateProviderConnection(connection.id, {\n    lastPingedResetAt: resetAt,\n    lastPingedResetKey: resetKey,\n    lastPingAt: new Date().toISOString(),\n    updatedAt: new Date().toISOString(),\n  });\n  console.log(`[AutoPing] ${provider}:${connection.id}: ping sent (reset ${resetAt})`);\n}\n\nfunction createDefaultDeps() {\n  return {\n    getSettings,\n    getProviderConnections,\n    updateProviderConnection,","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/shared/services/quotaAutoPing.js#L217-L253","documentation":"After a successful credential refresh and usage lookup, pingConnection sends a minimal 'ping' request upstream to start/confirm the quota window (handler.sendPing). If the upstream does not accept it (sendPing returns false — non-2xx response, provider rejected the tiny request, streaming setup failed), the failure is cached (cooldown) and this warning logs the reset time. lastPingedResetAt is deliberately NOT updated so the ping is retried after cooldown.","triggerScenarios":"handler.sendPing(connection, providerConfig, proxyOptions, deps) returns false: upstream chat/completions endpoint rejects the ping request (model unavailable, 429/5xx, auth scopes insufficient, account flagged), or response.ok is false; resetAt identifies the quota window that failed to be started.","commonSituations":"Provider outage or rate limiting at reset time; ping model removed/renamed in providerConfig; account lacking permission to call the ping model; streaming endpoint blocked by proxy; exhausted account that cannot accept even a tiny request.","solutions":["Wait for the failure cooldown to elapse — the ping is retried automatically on a later tick.","Check the provider account status/quota in its dashboard; a hard-exhausted or flagged account won't accept pings.","Verify providerConfig.pingModel is still valid for the account and adjust it if the model was renamed.","Test the provider endpoint manually with the same model; if 429/401/403, fix auth/plan issues before expecting the ping to succeed."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// sanity-check the ping model is accepted by the account before enabling auto-ping\nconst r = await fetch(baseUrl + \"/v1/chat/completions\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${token}`, \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({ model: providerConfig.pingModel, max_tokens: 1, messages: [{ role: \"user\", content: \"hi\" }] })\n});\nif (!r.ok) console.warn(\"ping model rejected by upstream:\", r.status);","typeGuard":null,"tryCatchPattern":"// failure cache + retry is built in; alert only if the same reset window keeps failing\nif (state.failureCache[key] && Date.now() - state.failureCache[key] > 3 * C.failureCooldownMs) {\n  console.warn(`[AutoPing] ${provider} persistent ping failure — check account/model`);\n}","preventionTips":["Keep providerConfig.pingModel set to a cheap, always-available model.","Check account quota/plan status in the provider dashboard for hard-exhausted accounts.","Investigate 429/5xx spikes on the provider before expecting pings to succeed.","Ensure lastPingedResetKey isn't manually cleared, causing retry storms against a rejecting upstream."],"tags":["quota","ping","upstream","rate-limit","provider"],"backgroundTag":"quota-ping-rejected","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}