shareAI-lab/learn-claude-code · error · GoalError

background result cannot be empty

Error message

background result cannot be empty

What it means

AgentSession.submit_background_result was called with text that is empty after stripping (s17_goal_loop/code.py:653). This method resumes the loop by appending a '[Background task completed]' user message carrying the background output; an empty body would resume the agent with no new information, so it is rejected.

Source

Thrown at s17_goal_loop/code.py:653

    def _summary_hook(messages: list[dict[str, Any]]) -> None:
        tool_count = sum(
            1
            for message in messages
            for block in (
                message.get("content")
                if isinstance(message.get("content"), list)
                else []
            )
            if isinstance(block, dict) and block.get("type") == "tool_result"
        )
        print(f"[hook] Stop: session used {tool_count} tool calls")
        return None

    async def submit_background_result(self, text: str) -> SessionResult:
        """Resume an active goal after the host receives background output."""

        if not text.strip():
            raise GoalError("background result cannot be empty")
        self.messages.append(
            {
                "role": "user",
                "content": f"[Background task completed]\n{text}",
            }
        )
        if self.goal.active is None:
            return SessionResult(text="", status="background_result")
        self.goal.begin_query()
        return await self._run_query()

    async def _run_query(self) -> SessionResult:
        turns = 0
        while True:
            if self.max_turns is not None and turns >= self.max_turns:
                self.trigger_hooks("Stop", self.messages)
                return SessionResult(
                    text="",

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Capture and check output before submitting; if empty, surface stderr or the exit code to the agent in the text
  2. Include the exit status in the text so it is never blank: f'exit={rc}\n{stdout}'
  3. If the background task genuinely produced nothing, submit a meaningful sentinel like '(no output, exit 0)' instead of an empty string

Example fix

# before
await session.submit_background_result(proc.stdout)

# after
stdout, stderr = proc.communicate()
text = stdout.strip() or stderr.strip() or f"(no output; exit {proc.returncode})"
await session.submit_background_result(text)
Defensive patterns

Strategy: validation

Validate before calling

text = (stdout or "").strip()
if not text:
    text = (stderr or "").strip() or f"(no output; exit {returncode})"
await session.submit_background_result(text)

Type guard

def is_submittable_background_result(text: object) -> bool:
    return isinstance(text, str) and bool(text.strip())

Try / catch

try:
    await session.submit_background_result(text)
except GoalError:
    text = "(background task produced no output)"
    await session.submit_background_result(text)

Prevention

When it happens

Trigger: Calling submit_background_result("") or submit_background_result(" \n "); forwarding a background process's stdout that was empty or only whitespace; calling it before the background task produced output.

Common situations: Host process wakes on a background-job-exit event and forwards captured stdout without checking it; background command failed with output on stderr only; race where the result is submitted before the writer flushed.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/444d7a925478e416. Report an issue: GitHub.