libnyanpasu/clash-nyanpasu · warning

no content-type

Error message

no content-type

What it means

cache_icon_inner downloads an icon over HTTP and needs the response's content-type header to determine the MIME type for the cache file. If the server returns a 2xx response without a Content-Type header, it throws anyhow "no content-type".

Solutions

  1. Fix the icon server to send a correct Content-Type header.
  2. Point the icon URL at a host serving proper MIME types.
  3. Fall back to a sniffed or default MIME type when the header is missing.
  4. Add the header via a reverse proxy in front of the icon host.

Example fix

// before
let mime = response.headers().get("content-type").ok_or(anyhow!("no content-type"))?.to_str()?.to_string();
// after
let mime = response.headers().get("content-type")
    .and_then(|v| v.to_str().ok())
    .unwrap_or("application/octet-stream")
    .to_string();
Defensive patterns

Strategy: fallback

Try / catch

try {
  await invoke('cache_icon', { url });
} catch (e) {
  if (String(e).includes('no content-type')) {
    usePlaceholderIcon(url);
  } else throw e;
}

Prevention

When it happens

Trigger: Caching a proxy-group/provider icon whose URL points to a server (bare object store, misconfigured CDN/nginx, raw file server) that omits Content-Type on a successful response.

Common situations: Subscription providers hosting icons on servers with no MIME types configured, self-hosted file servers, or services replying with headerless 200s.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/f25b04a7e20f2b95. Report an issue: GitHub.

Appendix: source

Thrown at backend/tauri/src/server/mod.rs:113

        Some(_) => {
            let span = tracing::span!(tracing::Level::DEBUG, "read_cache_file", path = ?cache_file);
            let _enter = span.enter();
            match read_cache_file(&cache_file).await {
                Ok((mime, bytes)) => return Ok((mime, bytes)),
                Err(e) => {
                    tracing::error!("failed to read cache file: {}", e);
                    remove_cache_file(&cache_file).await;
                }
            }
        }
        _ => (),
    }
    let client = get_reqwest_client()?;
    let response = client.get(url).send().await?.error_for_status()?;
    let mime = response
        .headers()
        .get("content-type")
        .ok_or(anyhow!("no content-type"))?
        .to_str()?
        .to_string();

    let bytes = response.bytes().await?;
    let data = CacheFile {
        mime: Cow::Owned(mime),
        bytes,
    };
    if let Err(e) = write_cache_file(&cache_file, &data).await {
        tracing::error!("failed to write cache file: {}", e);
    }
    Ok(data
        .try_into()
        .expect("It's impossible to fail, if failed, it must a bug, or memory corruption"))
}

#[tracing_attributes::instrument]
async fn cache_icon(query: Query<CacheIcon>) -> Response<Body> {

View on GitHub (pinned to f7dbce2997)