CrewAI ToolFailure: report a paid API error

Return a ToolFailure from a CrewAI tool when Sume refuses a job, so the run records a failure instead of a success, and set the policy to raise.

5 min readSume
All posts

When a CrewAI tool calls a paid API and the API refuses, return a ToolFailure instead of an error string. CrewAI's docs say a tool call that returns error text "worked", so the agent narrates the problem and the run is recorded as a success; a returned ToolFailure is recorded as a failure, and with tool_failure_policy set to raise it aborts the run.

The CrewAI facts come from its Tools page, read 2026-09-29. The Sume error codes come from Errors and rate limits and Generation admission. Sume has no CrewAI connector; the tool below is a plain HTTPS call.

How do I return a ToolFailure from a Sume call?

The docs show ToolFailure(message=..., code=..., retryable=...) returned from _run. Sume's error body has an error object with code, message, and request_id, so map those across. The attempt argument goes into the Idempotency-Key, which lets a retry after a capacity refusal use a new key.

import hashlib, os, requests
from crewai.tools import BaseTool
from crewai.tools.tool_failure import ToolFailure

RETRYABLE = {"rate_limited", "queue_full", "provider_capacity_exceeded"}

class StartVideo(BaseTool):
    name: str = "start_video"
    description: str = "Start a paid Sume video job. Returns the job id."

    def _run(self, prompt: str, attempt: int = 1):
        key = hashlib.sha256(prompt.encode()).hexdigest()[:16]
        r = requests.post(
            "https://api.sume.com/v1/videos", timeout=30,
            json={"model": "sume/auto", "prompt": prompt},
            headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
                     "Idempotency-Key": f"crew-{key}-{attempt}"})
        if r.ok:
            return r.json()
        err = r.json().get("error", {})
        code = err.get("code", str(r.status_code))
        return ToolFailure(
            message=f"Sume refused the job ({code}, {err.get('request_id')}): {err.get('message')}",
            code=code, retryable=code in RETRYABLE)

Which Sume errors are worth retrying?

The retryable flag is yours to set. This split follows Sume's error docs:

From Sume Errors and rate limits and Generation admission, read 2026-09-29.
StatusCodeRetryable?What to do
400invalid_requestNoFix the request
401unauthorizedNoFix the API key
402insufficient_creditsNoAdd funds or lower the request cost
409idempotency_conflictNoReuse a key only for an exact retry
429rate_limitedYesBack off; use retry-after when present
429queue_fullYes, laterWait for jobs to finish or cancel queued jobs
503provider_capacity_exceededYes, laterRetry later

Should the retry reuse the Idempotency-Key?

For an exact retry after a timeout, yes: the same key and body returns the original job. For queue_full and provider_capacity_exceeded the docs say to retry with the same key, but in current code the API rethrows the stored refusal of a failed job, so a same-key retry can replay the refusal. Treat that as current behavior and wait until the cause clears, then send a new key, which is why the sketch takes attempt.

How do I make a failure stop the crew?

The default policy is warn: it records the failure, emits ToolFailureDetectedEvent, and continues. raise records and emits, then aborts with ToolExecutionFailedError, and ignore does nothing. The most specific setting wins in the order tool, task, agent, crew, then warn, so you can set ToolFailurePolicy.RAISE on only the task that spends money.

Otherwise check result.has_tool_failures after crew.kickoff(). The docs say a crew can finish successfully with a non-empty tool_failures list, so check it before treating the output as complete.

What about a job that fails after it starts?

A submit that returns 202 is not a finished video. A job read is a normal response whose status can be failed; Sume's docs say to check the error field. Give the read tool the same shape: return the job body while it is pending or in_progress and completed, and a ToolFailure for failed, so the crew records it.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume