An expired media download URL does not prove that the file has been deleted. Check the job result, the access link and the stored object separately. If processing succeeded and the object still exists, recover access to that object before considering another processing job.
This guide is for developers whose application receives a completed media job but cannot open its delivery link. It focuses on diagnosing that handoff, rather than configuring storage from scratch. For destination selection and retention planning, start with the storage overview.
Record what actually failed
Keep your job ID and destination reference alongside your application's media record. A temporary URL should not be the only information available when a customer reports a failed download.
First retrieve the existing job through the job-status endpoint. A successful HTTP request to that endpoint is not the same as a successful media job. Inspect the returned status and delivery information. The s3_url field can carry a delivery URL; its name does not establish which storage provider holds the file.
Use the Python job tutorial to resume status lookup by ID. Do not submit the source again just because a browser could not download the result. Record which stage failed: status lookup, storage access or opening the downloaded media.
Check the link without changing its meaning
Use the returned URL unchanged, including the query string. A copy operation that truncates parameters can turn a valid link into an invalid request. Do not append your Tornado API key to a storage URL.
For Amazon S3, a presigned URL is tied to a request method and signing credentials. A URL can stop working when those credentials expire, even before its requested lifetime ends. Do not replace a signed GET request with HEAD as a generic availability test: the method is part of the signed request. See the S3 presigned URL documentation.
If a provider reports an expired token, investigate credentials and access renewal. If it reports a signature mismatch, check that the URL and request were preserved. Neither message alone establishes that the media needs to be generated again.
Inspect your stored object through an authorized client
For customer-controlled storage, use your normal authenticated storage client and the recorded bucket and object key. Do not reconstruct a key by trimming an unfamiliar delivery URL. For managed delivery where you do not have storage credentials, use the supported job access path or contact support with the job ID.
On Amazon S3, an authenticated HeadObject request reads metadata without downloading the body. A 403 is inconclusive about existence: S3 can return it for a missing object when the caller lacks s3:ListBucket. A 404 warrants checking the exact bucket, key and version; it is not permission to assume the original upload never happened. The HeadObject reference documents these distinctions.
Do not broaden bucket permissions merely to make a diagnostic return a different code. Ask the storage owner to verify the reference using an already authorized identity. A successful metadata lookup also does not validate the media's codec or prove that a downstream service can read it.
Keep recovery decisions explicit
The following Python example models decisions after you have collected evidence. It performs no network requests and contains no credentials. Its states are application-defined observations, not Tornado API response fields.
from enum import Enum
class Observation(Enum):
OBJECT_READABLE = "object_readable"
ACCESS_DENIED = "access_denied"
REFERENCE_NOT_FOUND = "reference_not_found"
FILE_ABSENT_CONFIRMED = "file_absent_confirmed"
UNKNOWN = "unknown"
def recovery_action(observation):
actions = {
Observation.OBJECT_READABLE: "recover_access_to_existing_object",
Observation.ACCESS_DENIED: "check_permissions_without_resubmitting",
Observation.REFERENCE_NOT_FOUND: "verify_bucket_key_and_version",
Observation.FILE_ABSENT_CONFIRMED: "review_restore_or_reprocess",
Observation.UNKNOWN: "collect_more_evidence",
}
return actions[observation]
assert recovery_action(Observation.OBJECT_READABLE) == (
"recover_access_to_existing_object"
)
assert recovery_action(Observation.ACCESS_DENIED) == (
"check_permissions_without_resubmitting"
)
assert recovery_action(Observation.REFERENCE_NOT_FOUND) == (
"verify_bucket_key_and_version"
)
assert recovery_action(Observation.FILE_ABSENT_CONFIRMED) == (
"review_restore_or_reprocess"
)
assert recovery_action(Observation.UNKNOWN) == "collect_more_evidence"
The example was checked with these synthetic assertions. It does not test a live bucket, refresh a Tornado delivery URL or guarantee recovery. Your storage integration must supply the observations. In particular, do not map every 403 to a missing file or every 404 to confirmed deletion.
For a readable object, obtain supported access for the intended consumer and retry the delivery stage. For confirmed absence, review available versions or backups before deciding whether to process the source again. Reprocessing is a separate decision that depends on source availability, authorization, account conditions and your application's needs.
Prevent the same incident in queued consumers
A worker may receive a link long before it starts consuming the file. Design the handoff so it retains a stable job or object reference and checks access when work begins. Avoid embedding a temporary link as the permanent media identifier in a long-lived queue.
Keep diagnostic logs useful without storing bearer links: record the job ID, stage, timestamp and sanitized provider error category. Restrict any detailed access evidence to the people who need it. A successful retry should update the delivery outcome without erasing the original failure.
If the file downloads but your transcription or editing service rejects it, move to format validation. That is a different failure from URL expiry. The audio handoff guide explains how to check consumer requirements.
Test the recovery path before scaling
Use one file you are authorized to process. Confirm the successful path, then simulate an unusable cached link in your application and verify that it inspects the existing record instead of immediately creating another job. Keep simulation results separate from live storage checks.
Open the Tornado API dashboard to inspect an existing job and its destination. If the result and storage evidence disagree, contact support with the job ID and a sanitized description of the failed stage. Resolve access first; then resume the downstream workflow.