Avatar preview failed on Sume MCP: stop_and_ask, do not generate video
When an avatar video preview fails or is canceled, Sume MCP returns recovery code stop_and_ask: do not call generate_video, report the reason, offer a rewrite.
If an avatar video preview fails or is canceled on Sume's hosted MCP, the tool result carries agent.recovery.code: stop_and_ask. The agent must not call avatar-video-previews_generate_video, must report the provider reason in the user's language, and should offer a scene or script rewrite instead of retrying the identical payload.
The two-stage flow
Avatar video previews split the work. The create call produces stills. After the user approves them, avatar-video-previews_generate_video is called with an empty payload, because admission uses the stored preview, and then jobs_wait follows. The server's next-step list for avatar-video-previews_create says exactly this, including a STOP for still approval.
So a failed preview is the cheap place to stop. Calling generate-video anyway would start the expensive stage on stills that do not exist.
Two different rejections
The hint separates two policy rejections that look alike to a model: an image-policy reject at the preview stage, which comes from the still-image model, and a video-policy reject at the generate-video stage, which comes from the video model. They need different edits. A preview reject means rewrite the scene description or the reference. A generate-video reject means the approved stills passed but the motion or script did not.
| Observed | Recovery code | Agent action |
|---|---|---|
| Preview status failed or canceled | stop_and_ask | Stop. No generate_video. Report reason. |
| Stills ready, user approves | none | Call generate_video with payload {} |
| Preview still processing | none | Poll avatar-video-previews_get later |
Branching in your harness
Route on the recovery code, not on free text.
def next_action(result: dict) -> str:
agent = result.get('agent') or {}
recovery = agent.get('recovery') or {}
if recovery.get('code') == 'stop_and_ask':
return 'ask_user: ' + recovery.get('hint', '')
step = agent.get('next_step') or {}
return 'call: ' + step.get('tool', 'none')
print(next_action({'agent': {'recovery': {'code': 'stop_and_ask', 'hint': 'Preview failed'}}}))Limits
This recovery applies to failed or canceled preview reads. A running preview is not a failure, so keep polling. Where a job fails for another reason, read the failure through jobs_get, as in the failed job post, because jobs_result returns 409 on a failed job.
Report the reason without quoting prompts or signed URLs. Sume's results tell agents not to echo them in reports.
Why not just retry
A retry of the identical preview payload runs into the same decision. The server hint is explicit that the identical payload must not be auto-retried. A rewrite changes the inputs, so treat it as a new request with its own idempotency_key rather than reusing the old one. Keep the failed preview id in your notes so the user can see what was changed between attempts.
If you run unattended, stop_and_ask is a good place to hand off to a person. A model can draft the rewrite, but a human or a policy check should approve it, since the generate-video stage is the expensive one.
Checklist before you ship
- Branch on agent.recovery.code, never on the wording of the hint.
- Block the generate_video tool in your harness while a preview is failed or canceled.
- Show the user the provider reason in their language, without prompts or signed URLs.
- Offer one rewrite, and run it as a new preview with its own idempotency_key.
Sources
Related posts
More in Developers
- timeline_get 409 job_not_completed on Sume MCP: retry, do not give up
A 409 job_not_completed from timeline_get means the render is still running, so retry. Call jobs_wait on the same job_id; never report the run blocked.
- Sume MCP tools_schema safety object: build a tool allowlist from it
Sume's tools_schema returns a safety object per tool: paid_generation, read_only, requires_idempotency_key and more. Build your agent allowlist from it.
- Why a Sume output schema is rejected: the strict subset rules
Sume saves an output schema only in the strict subset: object root, additionalProperties false, all properties required, nullable unions, 10 levels.
- Sume queue position and ETA: none exists, poll generation_limits
Sume shows queue counts and remaining capacity, not a per-job position or ETA. Read generation_limits and keep new work inside the headroom formula.
Written by Sume