hermes - 💡(How to fix) Fix Bug: `_generate_summary` returns None when max_retries=0, injected as conversation content [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

_generate_summary() in trajectory_compressor.py has a missing return path. When max_retries is 0, the function returns None instead of a string. This None is then injected into the compressed conversation as a message value, causing downstream TypeError crashes.

Error Message

def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str: prompt = f"..."

for attempt in range(self.config.max_retries):   # range(0) = empty
    try:
        ...
        return self._normalize_summary(raw)
    except Exception:
        ...
# ← No return statement here — implicit return None

Root Cause

In trajectory_compressor.py, lines 571-638:

def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str:
    prompt = f"..."
    
    for attempt in range(self.config.max_retries):   # range(0) = empty
        try:
            ...
            return self._normalize_summary(raw)
        except Exception:
            ...
    # ← No return statement here — implicit return None

When max_retries=0, range(0) produces an empty sequence, the loop body never executes, and the function falls through to an implicit return None.

The None is then used at lines 795-812:

summary = self._generate_summary(content_to_summarize, metrics)  # None

compressed.append({
    "from": "human",
    "value": summary    # None injected as message content
})

Downstream code that expects string operations on value (e.g., len(), concatenation, encoding) will crash with TypeError.

Fix Action

Fixed

Code Example

def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str:
    prompt = f"..."
    
    for attempt in range(self.config.max_retries):   # range(0) = empty
        try:
            ...
            return self._normalize_summary(raw)
        except Exception:
            ...
    # ← No return statement here — implicit return None

---

summary = self._generate_summary(content_to_summarize, metrics)  # None

compressed.append({
    "from": "human",
    "value": summary    # None injected as message content
})

---

# Simulate the behavior
def _generate_summary(max_retries):
    for attempt in range(max_retries):
        return "summary text"
    # implicit return None

result = _generate_summary(0)
print(result)       # None
print(len(result))  # TypeError: object of type 'NoneType' has no len()

---

def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str:
    prompt = f"..."
    
    for attempt in range(self.config.max_retries):
        try:
            ...
            return self._normalize_summary(raw)
        except Exception:
            ...
    
    # Fallback when all retries exhausted or max_retries=0
    logger.warning("Summary generation failed after %d attempts; using placeholder", self.config.max_retries)
    return self._normalize_summary(None)
RAW_BUFFERClick to expand / collapse

Summary

_generate_summary() in trajectory_compressor.py has a missing return path. When max_retries is 0, the function returns None instead of a string. This None is then injected into the compressed conversation as a message value, causing downstream TypeError crashes.

Root Cause

In trajectory_compressor.py, lines 571-638:

def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str:
    prompt = f"..."
    
    for attempt in range(self.config.max_retries):   # range(0) = empty
        try:
            ...
            return self._normalize_summary(raw)
        except Exception:
            ...
    # ← No return statement here — implicit return None

When max_retries=0, range(0) produces an empty sequence, the loop body never executes, and the function falls through to an implicit return None.

The None is then used at lines 795-812:

summary = self._generate_summary(content_to_summarize, metrics)  # None

compressed.append({
    "from": "human",
    "value": summary    # None injected as message content
})

Downstream code that expects string operations on value (e.g., len(), concatenation, encoding) will crash with TypeError.

Reproduction

# Simulate the behavior
def _generate_summary(max_retries):
    for attempt in range(max_retries):
        return "summary text"
    # implicit return None

result = _generate_summary(0)
print(result)       # None
print(len(result))  # TypeError: object of type 'NoneType' has no len()

Suggested Fix

Add a fallback return after the retry loop:

def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str:
    prompt = f"..."
    
    for attempt in range(self.config.max_retries):
        try:
            ...
            return self._normalize_summary(raw)
        except Exception:
            ...
    
    # Fallback when all retries exhausted or max_retries=0
    logger.warning("Summary generation failed after %d attempts; using placeholder", self.config.max_retries)
    return self._normalize_summary(None)

The same issue exists in the async variant _generate_summary_async() (lines 640-707).

Files

trajectory_compressor.py, lines 571-638 (sync), 640-707 (async), 795-812 (usage)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING