@@ -2351,6 +2351,19 @@ func (m *Manager) MarkResult(ctx context.Context, result Result) {
23512351 state .NextRetryAfter = next
23522352 suspendReason = "model_not_supported"
23532353 shouldSuspendModel = true
2354+ } else if isCloudflareChallengeResultError (result .Error ) {
2355+ next , backoffLevel := nextCloudflareCooldown (state .Quota .BackoffLevel , disableCooling , now )
2356+ state .NextRetryAfter = next
2357+ state .StatusMessage = "cloudflare challenge"
2358+ if auth .LastError != nil {
2359+ auth .StatusMessage = "cloudflare challenge"
2360+ }
2361+ state .Quota = QuotaState {
2362+ Exceeded : true ,
2363+ Reason : "cloudflare challenge" ,
2364+ NextRecoverAt : next ,
2365+ BackoffLevel : backoffLevel ,
2366+ }
23542367 } else {
23552368 switch statusCode {
23562369 case 401 :
@@ -2750,6 +2763,42 @@ func isModelSupportResultError(err *Error) bool {
27502763 return isModelSupportErrorMessage (err .Message )
27512764}
27522765
2766+ func isCloudflareChallengeErrorMessage (message string ) bool {
2767+ lower := strings .ToLower (strings .TrimSpace (message ))
2768+ return strings .Contains (lower , "challenge-platform" ) ||
2769+ strings .Contains (lower , "cf-mitigated" ) ||
2770+ strings .Contains (lower , "cloudflare challenge" ) ||
2771+ (strings .Contains (lower , "cloudflare" ) && strings .Contains (lower , "<html" ))
2772+ }
2773+
2774+ func isCloudflareChallengeError (err error ) bool {
2775+ if err == nil {
2776+ return false
2777+ }
2778+ return isCloudflareChallengeErrorMessage (err .Error ())
2779+ }
2780+
2781+ func isCloudflareChallengeResultError (err * Error ) bool {
2782+ if err == nil {
2783+ return false
2784+ }
2785+ return isCloudflareChallengeErrorMessage (err .Message )
2786+ }
2787+
2788+ func nextCloudflareCooldown (backoffLevel int , disableCooling bool , now time.Time ) (time.Time , int ) {
2789+ var next time.Time
2790+ if ! disableCooling {
2791+ cooldown , nextLevel := nextQuotaCooldown (backoffLevel , disableCooling )
2792+ if cooldown < 10 * time .Second {
2793+ cooldown = 10 * time .Second
2794+ }
2795+ if cooldown > 0 {
2796+ next = now .Add (cooldown )
2797+ }
2798+ backoffLevel = nextLevel
2799+ }
2800+ return next , backoffLevel
2801+ }
27532802func isRequestScopedNotFoundMessage (message string ) bool {
27542803 if message == "" {
27552804 return false
@@ -2777,6 +2826,9 @@ func isRequestInvalidError(err error) bool {
27772826 if err == nil {
27782827 return false
27792828 }
2829+ if isCloudflareChallengeError (err ) {
2830+ return false
2831+ }
27802832 if isModelSupportError (err ) {
27812833 return false
27822834 }
@@ -2818,6 +2870,18 @@ func applyAuthFailureState(auth *Auth, resultErr *Error, retryAfter *time.Durati
28182870 }
28192871 }
28202872 statusCode := statusCodeFromResult (resultErr )
2873+ if isCloudflareChallengeResultError (resultErr ) {
2874+ auth .StatusMessage = "cloudflare challenge"
2875+ next , backoffLevel := nextCloudflareCooldown (auth .Quota .BackoffLevel , disableCooling , now )
2876+ auth .Quota = QuotaState {
2877+ Exceeded : true ,
2878+ Reason : "cloudflare challenge" ,
2879+ NextRecoverAt : next ,
2880+ BackoffLevel : backoffLevel ,
2881+ }
2882+ auth .NextRetryAfter = next
2883+ return
2884+ }
28212885 switch statusCode {
28222886 case 401 :
28232887 auth .StatusMessage = "unauthorized"
0 commit comments