Skip to content

Commit 938458e

Browse files
committed
fix: prevent infinite reflector recursion by moving retry guards to reflector entry points
1 parent a64410f commit 938458e

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

backend/pkg/providers/performer.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -406,15 +406,6 @@ func (fp *flowProvider) callWithRetries(
406406
"agent_type": optAgentType,
407407
}))
408408

409-
// Check if we are already in a reflector retry cycle to prevent recursion.
410-
if isReflectorRetry(ctx) {
411-
logger.Error("detected reflector recursion attempt, preventing infinite retry loop")
412-
return nil, errors.New("reflector recursion detected: cannot retry after reflector advice failed")
413-
}
414-
415-
// Mark context as being in retry cycle for next invocation (via reflector).
416-
ctx = markReflectorRetry(ctx)
417-
418409
ticker := time.NewTicker(delayBetweenRetries)
419410
defer ticker.Stop()
420411

@@ -699,6 +690,13 @@ func (fp *flowProvider) performReflector(
699690
}
700691
chain = append(chain, reflectorMsg)
701692
if len(result.funcCalls) == 0 {
693+
// Check if we are already in a reflector retry cycle to prevent infinite recursion.
694+
// This blocks recursive performReflector calls after caller reflector was invoked.
695+
if isReflectorRetry(ctx) {
696+
logger.Error("reflector recursion detected: cannot recursively call reflector after caller reflector")
697+
return nil, errors.New("reflector recursion detected: LLM returned no tool calls after reflector advice")
698+
}
699+
702700
return fp.performReflector(ctx, optOriginType, chainID, taskID, subtaskID, chain, executor,
703701
humanMessage, result.content, executionContext, iteration+1)
704702
}
@@ -726,6 +724,21 @@ func (fp *flowProvider) performCallerReflector(
726724
"msg_chain_id": chainID,
727725
"errors_count": len(errs),
728726
})).WithError(errors.Join(errs...))
727+
728+
// Check if we are already in a reflector retry cycle to prevent infinite recursion.
729+
// This blocks repeated calls to performCallerReflector after reflector advice failed.
730+
if isReflectorRetry(ctx) {
731+
logger.Error("reflector recursion detected: caller reflector already invoked in this chain")
732+
return nil, errors.New("reflector recursion detected: cannot invoke caller reflector again after reflector advice failed")
733+
}
734+
735+
// Mark context to prevent any further reflector recursion.
736+
// This flag will be checked in:
737+
// 1. performCallerReflector (here) - if reflector advice fails again
738+
// 2. performReflector - before recursive call when no tool calls returned
739+
ctx = markReflectorRetry(ctx)
740+
logger = logger.WithContext(ctx)
741+
729742
logger.Warn("max retries reached, invoking caller reflector for guidance")
730743

731744
reflectorContent := fmt.Sprintf(

0 commit comments

Comments
 (0)