Valibot safeParse on a Sume job status: keep polling on bad data
Valibot's safeParse returns a result instead of throwing, so a malformed Sume status body can be logged and retried. A short schema and runnable code.

The answer
Valibot's introduction says that apart from parse, which throws, it offers a non-exception-based API with safeParse. For a poll loop that is the better fit: a bad or surprising status body becomes a value you can log and skip, not an exception that kills the loop.
Define a small schema for just the fields you use from a Sume status response: terminal, result_ready and next_poll_after_seconds. Extra fields are not a problem.
The schema
Sume wraps success bodies in data. The OpenAPI marks next_poll_after_seconds as nullable, so the schema must allow null. This runs as written with npm i valibot and a TypeScript runner.
import * as v from 'valibot';
const Status = v.object({
data: v.object({
terminal: v.boolean(),
result_ready: v.boolean(),
next_poll_after_seconds: v.nullable(v.number()),
}),
});
type Status = v.InferOutput<typeof Status>;
const body = JSON.parse(
'{"data":{"terminal":false,"result_ready":false,"next_poll_after_seconds":3}}',
);
const r = v.safeParse(Status, body);
if (r.success) {
const s: Status = r.output;
console.log(s.data.next_poll_after_seconds);
} else {
console.log('bad payload', r.issues.length);
}What the loop does with a failure
If safeParse fails once, treat it as a transient problem: log the issues, wait a default interval and read again. If it fails several times in a row, stop and surface an error, since a persistent mismatch means the API or your schema has changed. Never treat a parse failure as proof that the job failed; the job may be fine.
| Outcome | Reaction |
|---|---|
| success, terminal false | Wait next_poll_after_seconds, read again |
| success, terminal true, result_ready true | Fetch /result |
| success, terminal true, result_ready false | Read events; the job failed or was canceled |
| safeParse fails | Log issues, retry a few times, then alert |
Why not trust the types alone
A generated type describes the spec you fetched. A runtime schema checks what actually arrives, which matters when a proxy returns an HTML error page with a 200, or when the API adds a state. Both are cheap to guard against with a few lines at the boundary.
Keep the schema minimal
Validate only what you read. A schema that mirrors the whole response fails on harmless additions; a small one stays stable. Compare it to the spec now and then, as in the contract test post.
Where to put the parsed value
Return the parsed output from one function and have the rest of the code depend on that function only. If Sume adds a field, nothing changes. If it renames one you read, exactly one file breaks and the failure shows up in a test rather than in production.
Valibot's page highlights a small bundle size and type inference through InferOutput, which is why the same schema can serve as both your runtime check and your TypeScript type. You never maintain two definitions that can drift apart.
Also validate the create response with a second schema. It carries status_url, result_url and idempotency_hit; a missing URL there is a different kind of failure than a bad status, and deserves a clear error before you start a poll loop that cannot work.
Sources
Related posts
More in Developers
- Validate video duration and resolution in Python before you submit
Fetch GET /v1/videos/models and check duration, resolution and aspect_ratio per model in about 25 lines of Python, before a Sume video job fails.
- Veo 2.0 and Veo 3.0 shut down June 30: what model id to call now
Google retired veo-2.0 and veo-3.0 ids on 2026-06-30. See which Veo and Omni ids the Gemini docs list now, and how to move the call to Sume.
- Edited a script? Reuse unchanged TTS takes with verify-spine
After a script edit, Sume's read-only verify-spine route checks which TTS takes still cover the accepted sentences, so you regenerate only what changed.
- verifyWebhook returns false when a header arrives as an array
verifyWebhook refuses an array-valued signature header unless it holds exactly one value. How to normalise headers so a rotation window still verifies.
Written by Sume