Scenema-Audio: Gibberish Output and Hugging Face SSL/403 Download error

Hi people,
I’m trying to run Scenema Audio locally with Podman. I’m facing two issues:

Gibberish output: The model generates unintelligible audio instead of the expected speech.

Hugging Face download: The ~4.9 GB transformer checkpoint fails with SSL: CERTIFICATE_VERIFY_FAILED, Read timed out, and eventually 403 Forbidden – Make sure your token has the correct permissions.

My Hugging Face token is working (whoami() returns successfully), and I’ve also added the corporate CA certificate provided by IT. A direct HTTPS test to Hugging Face returns 200, but the model download still fails.

Has anyone faced these issues or knows the correct fix, particularly for the HF CDN/Xet download?

Hmm… For now, I tried a few things:


I am not sure these identify the root cause, but I think there are probably two issues worth separating initially:

  1. the SSL / timeout / 403 during the Hugging Face download, and
  2. the gibberish / unintelligible audio.

The download side looks especially worth splitting by which endpoint is actually failing. A successful whoami() and a 200 from huggingface.co are useful checks, but they do not test the whole model-file delivery path.

Hugging Face’s current proxy/firewall documentation explicitly says model contents can be served from separate Xet/CAS/CDN hosts, and that huggingface.co alone is not sufficient to allowlist.

I did a small neutral-network test against the public Scenema assets, and the same ~4.9 GB INT8 checkpoint was redirected away from huggingface.co; across separate requests I observed both HF’s AWS and GCP CDN hosts. So I would probably start by identifying the exact hostname that produced the certificate error, the timeout, and the final 403, rather than interpreting the final 403 by itself.

For the gibberish side, I also tried a short, no-reference baseline against the public Scenema ZeroGPU deployment. At the Space revision I tested, the short fixed-seed generations were intelligible; one seed was transcribed exactly by an independent Whisper model, while another had only small word substitutions. That obviously does not reproduce your Podman environment, but it makes me inclined to treat the gibberish as a separate branch first rather than assume the failed download directly caused it.

So my default path would be roughly:

Download problem:
    identify the actual failing endpoint
        ↓
    inspect proxy / CA / client state inside the running container
        ↓
    compare Xet enabled vs disabled
        ↓
    only then narrow it to auth vs TLS/proxy vs CDN/storage

Gibberish:
    short + no reference + fixed seed + validation explicitly enabled
        ↓
    if clear: add the more complex conditions back one at a time
    if still broken: look at local model/runtime state

The two quickest pieces of information seem to be the failing hostname and whether the simplest local generation is still unintelligible.

What I actually tried

1. Small download-path control

I did not try to reproduce a corporate proxy or CA environment. Instead, I used a neutral Colab environment to see what the public Scenema assets actually do today.

For the public ScenemaAI/scenema-audio repo I tested:

  • the ~4.9 GB scenema-audio-transformer-int8.safetensors,
  • and the much smaller ~42.7 MB Xet-backed VAE checkpoint.

For the 4.9 GB file, I only fetched a small byte range rather than downloading 4.9 GB repeatedly.

The interesting part was the route:

huggingface.co
    ↓ redirect
HF CDN

Across separate requests I observed both:

us.aws.cdn.hf.co
us.gcp.cdn.hf.co

for the same public Scenema asset.

That seems relevant because the current HF firewall guide lists those CDN hosts separately from:

huggingface.co
cas-server.xethub.hf.co
transfer.xethub.hf.co

and explains that a download may fail even when huggingface.co itself is reachable.

I also tested the smaller Xet-backed Scenema file under four combinations:

Hub client Xet Result
huggingface_hub 0.36.2 enabled success
huggingface_hub 0.36.2 disabled success
newer huggingface_hub enabled success
newer huggingface_hub disabled success

All four downloaded the same expected file successfully and produced the expected checksum.

That does not mean an unrestricted 42 MB transfer proves a restricted 4.9 GB transfer will succeed. I mainly used it as a cheap control for the client/storage path.

One useful observation was that disabling Xet changed the transfer path, but it did not mean the CDN disappeared from the path. So I would treat HF_HUB_DISABLE_XET=1 as a diagnostic comparison, not as a binary “Xet / not Xet” oracle.

2. Small generation control

I also called the public multimodalart/scenema-audio ZeroGPU Space from another HF Space.

I pinned this observation to the Space revision I actually tested:

b89b02d763ce583c2a0b95aa89d805a6288bcca4

The baseline was deliberately simple:

short English sentence
no reference voice
closeup
no background SFX
fixed seed
validation explicitly enabled

I generated:

/generate      seed 42
/generate      seed 43
/voice_design  seed 42

All three returned intelligible speech.

As a rough independent control I transcribed the resulting WAVs with a separate CPU faster-whisper model.

For one /generate seed, the independent transcript matched the expected sentence exactly. For the other seed it made only a couple of small word substitutions. /voice_design was also clearly intelligible.

I would not read much into the particular seed numbers. The useful point to me is simply that the simple public baseline was not gibberish, and even this tiny test showed some seed-dependent variation.

The public Space has changed since that test, so I would treat the revision above as the actual observation rather than assume today’s main is bit-for-bit identical.

Why huggingface.co returning 200 does not test the whole download path

HF now documents the file-delivery path explicitly in Downloading models behind a proxy or firewall.

At the time of writing, the documented HTTPS endpoints include:

huggingface.co
cas-server.xethub.hf.co
cas-server.xethub-eu.hf.co
transfer.xethub.hf.co
transfer.xethub-eu.hf.co
us.aws.cdn.hf.co
us.gcp.cdn.hf.co
cdn-lfs-us-1.hf.co
cdn-lfs-eu-1.hf.co

So there are several logically different checks:

Hub API / metadata works
        ≠
Xet token/CAS works
        ≠
storage transfer works
        ≠
CDN object fetch works

That is why I would not take:

whoami() succeeds
https://huggingface.co -> 200

as evidence that the complete large-file route is clear.

HF’s documentation also specifically notes that a ReadTimeoutError partway through a download can indicate that the initial connection worked while a storage/CDN host is blocked.

There is another enterprise-network detail that may be relevant: wildcard matching is proxy-dependent.

For example, HF notes that on a proxy that only supports a single-label wildcard,

*.hf.co

might match:

cdn-lfs-us-1.hf.co

but not a deeper hostname such as:

us.aws.cdn.hf.co
us.gcp.cdn.hf.co
cas-server.xethub.hf.co

So even “we already allowlisted *.hf.co” may or may not mean what it appears to mean, depending on the corporate proxy’s wildcard semantics.

Where security policy permits it, HF currently recommends suffix-based allowlisting of hf.co and huggingface.co; otherwise the explicit endpoint table in the documentation is the safer reference.

How I would split the SSL / timeout / 403

I think the hostname + error type is more informative than the status code alone.

Something like this:

Where did it fail?

huggingface.co or an auth/token endpoint
    └─> Hub authentication / repository access branch

cas-server.xethub... + certificate verification failure
    └─> corporate TLS inspection / CA trust / proxy branch

transfer.xethub... or *.cdn.hf.co + read timeout
    └─> firewall / proxy / routing / slow-transfer branch

signed CDN URL + 403
    └─> inspect the actual server error before interpreting it
        as an HF token-permission failure

Why I would not interpret the 403 from 403 alone

There are legitimate cases where a 403 really does mean authorization failure.

But a 403 from a CDN object URL is not necessarily the same thing as a 403 from a Hub authentication endpoint.

As one concrete counterexample, this recent Xet issue documented a case where:

  • the Hub API was healthy,
  • the Xet token request succeeded,
  • CAS requests succeeded,
  • but us.gcp.cdn.hf.co returned a signed-URL 403 SignatureError.

In that case, authentication with a valid HF token did not change the failure:

Xet issue #897

That issue is closed, and I am not suggesting it is your bug. I only think it is a useful example of why the layer producing the 403 matters.

If your 403 is preceded by certificate errors and timeouts, I would especially want to know whether all three messages are actually coming from the same host.

What is safe/useful to capture

You probably do not need to post a complete signed URL.

These are usually enough to classify the branch:

hostname
path type
HTTP status
sanitized server error message
Request ID, if present

I would redact:

Authorization headers
HF tokens
proxy credentials
signed URL query strings
internal corporate names if sensitive

before posting logs publicly.

Container-side proxy / CA / client checks

Since this is Podman, I would trust checks made inside the running container more than equivalent checks on the host.

Podman’s current documentation says proxy variables are passed into containers by default when they are present in the Podman process environment, but later container settings such as --env-file or --env can override them:

Podman run documentation

So rather than assuming the proxy is or is not inherited, I would inspect the effective state.

For example, without printing any secret values:

podman exec -i <container> python - <<'PY'
import os

for name in (
    "HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY",
    "http_proxy", "https_proxy", "no_proxy",
):
    print(f"{name}: {'SET' if os.environ.get(name) else 'unset'}")
PY

For the actual HF client versions:

podman exec -i <container> python - <<'PY'
from importlib.metadata import PackageNotFoundError, version

for package in ("huggingface-hub", "hf-xet"):
    try:
        print(package, version(package))
    except PackageNotFoundError:
        print(package, "not installed")
PY

I would check the actual installed versions instead of trying to infer them only from the Dockerfile.

For Python’s default TLS paths:

podman exec -i <container> python - <<'PY'
import ssl
print(ssl.get_default_verify_paths())
PY

And, if openssl is present in the container, a TLS-only check against whichever host actually failed can be useful:

podman exec -i <container> \
  openssl s_client \
  -connect <HOST>:443 \
  -servername <HOST> \
  </dev/null

This is particularly useful if a corporate TLS-inspection appliance is re-signing certificates.

Scenema’s image is Ubuntu-based. Ubuntu’s documented system trust-store route is:

/usr/local/share/ca-certificates/*.crt
        ↓
update-ca-certificates
        ↓
/etc/ssl/certs/ca-certificates.crt

See Ubuntu’s root-CA trust-store guide.

I would avoid solving this by globally disabling TLS verification. If the failure is a trust-chain/proxy-boundary problem, disabling verification would hide the distinction that is useful for diagnosing it.

A small Xet comparison, if useful

Hugging Face currently documents:

HF_HUB_DISABLE_XET=1

as the switch for disabling hf-xet even when it is installed:

huggingface_hub environment variables

I would interpret the comparison like this:

normal path fails
HF_HUB_DISABLE_XET=1 succeeds
    → Xet-specific client/path becomes a stronger candidate

normal path fails
HF_HUB_DISABLE_XET=1 also fails
    → do not conclude "Xet/network is ruled out"
      because the fallback can still use the same HF CDN infrastructure

One small gotcha: current huggingface_hub documentation says its environment variables are read at import time.

So if testing this in Python, I would set the environment variable before starting/importing the HF client, rather than toggling it after huggingface_hub is already imported.

For deeper logging, HF also documents:

HF_DEBUG=1

which logs HF requests as equivalent cURL commands.

That can be useful for seeing the request sequence, but I would sanitize the logs before posting them.

How I would separate the gibberish side

Scenema’s own README already documents several quality failure modes:

  • occasional garbling of complex multi-syllable words / proper nouns,
  • quality degradation when individual generated segments get too long,
  • multilingual pronunciation problems,
  • sensitivity to reference-audio quality.

See the current Scenema Audio README.

So I would first try to answer a smaller question:

Does the simplest local generation path produce intelligible speech?

A useful baseline would be:

short ordinary sentence
no reference voice
shot = closeup
background SFX = false
fixed seed
validation explicitly enabled

I would explicitly set the validation flag rather than assume a default.

There are multiple interfaces around Scenema, and the defaults have not always been identical. For example, the local API documentation currently describes validate=true as the default, while the public Gradio Space revision I tested exposed the validation checkbox as off by default. The current Space has since changed it to on.

So for diagnosis I think:

validate = true

is better than relying on whatever a particular UI/API revision calls “default”.

If the short baseline is intelligible

Then I would add the more complicated conditions back according to what your real use case needs.

short baseline works
│
├─ longer text fails
│     → chunking / sentence boundaries / long-form continuity
│
├─ reference voice causes the failure
│     → reference quality / voice-conditioning / SeedVC branch
│
└─ only some seeds fail
      → stochastic pronunciation / generation branch

Scenema’s documentation says text is automatically split around a ~15-second generation window and notes increased repetition/pronunciation failures beyond that range.

It also describes SeedVC as part of the voice-identity/cross-chunk path, especially when using a reference voice or multiple chunks.

So a clean short/no-reference sample removes several moving parts at once without requiring a large experiment matrix.

If the short baseline is still gibberish

Then I would move local state/runtime higher on the list:

simple local baseline still unintelligible
│
├─ validation was actually off
│     → retry explicitly with validation
│
├─ models came from an existing persistent volume
│     → check model state / integrity / timeline
│
└─ fresh/verified assets behave the same way
      → core local runtime / generation branch

A small seed note

In my tiny public-Space control, changing only the fixed seed changed the independent transcription slightly.

I would not infer that any particular seed is “good” or “bad”; it is just consistent with Scenema’s own use of a new seed when Whisper validation rejects a generation.

Model volume / checkpoint sanity checks

I would keep this as a secondary check rather than assume the checkpoint is corrupt.

The reason it may still be worth checking is that Scenema’s documented Docker workflow downloads roughly 38 GB on first startup and then reuses a Docker volume on later starts.

So before connecting:

download failed

and

gibberish was generated

as one causal chain, I would first verify that both observations actually came from the same model-volume state.

For example:

Was the gibberish generated:
    before the failed download?
    after it?
    after restarting?
    from an already-populated model volume?

If the 4.9 GB transformer file already exists locally, a direct checksum comparison is a cheap way to eliminate one branch.

For example:

sha256sum /app/models/scenema-audio-transformer-int8.safetensors

and compare it against the current file metadata on the Scenema model repo.

I would view that as a sanity check, not as a prediction that corruption is the cause.

There is also a separate access requirement later in the full local stack: Scenema uses gated Gemma 3 12B and documents that an HF token with accepted Gemma access is required. That is worth keeping conceptually separate from whether the public Scenema transformer blob itself can be fetched.

A few related references

These are the references I found most useful; I would not assume the linked issues are the same root cause.

So, if I were narrowing this down with the least work first, I would probably do only these three things:

1. Identify the hostname that produced each SSL / timeout / 403 error.

2. Check the effective CA / proxy / huggingface_hub / hf_xet state
   inside the running Podman container.

3. Try one short, no-reference, fixed-seed generation with validation
   explicitly enabled.

Those three observations should separate a fairly large number of possibilities without requiring a large rebuild or a long matrix of experiments.