tonhowtf/omniget · error

describe

Error message

describe: {}

What it means

design_speak POSTs the natural-language description to {base}/design/describe and calls error_for_status(); any non-success response is wrapped as 'describe: {}'. It means the server rejected the voice-design description request that produces the synthesis instruct parameters.

Solutions

  1. Check the wrapped status in the error: 401/403 → credentials; 400/422 → description content.
  2. Ensure opts.description is non-empty and within the server's length limits before calling.
  3. Verify API key and base URL configuration.
  4. Confirm the VoiceStudio version still exposes /design/describe (endpoint may have changed).
  5. Retry with backoff on 429/5xx.

Example fix

// before
let j: serde_json::Value = c
    .post(format!("{}/design/describe", b))
    .json(&serde_json::json!({ "description": opts.description }))
    .send()
    .await?
    .error_for_status()
    .map_err(|e| anyhow!("describe: {}", e))?;
// after
anyhow::ensure!(!opts.description.trim().is_empty(), "describe: descricao vazia");
let resp = c.post(format!("{}/design/describe", b))
    .json(&serde_json::json!({ "description": opts.description }))
    .send().await?;
let j: serde_json::Value = resp.error_for_status()
    .map_err(|e| anyhow!("describe: {}", e))?
    .json().await?;
Defensive patterns

Strategy: validation

Validate before calling

let desc = opts.description.trim();
if desc.is_empty() {
    return Err("description must not be empty".into());
}
if desc.len() > 2000 {
    return Err("description too long".into());
}

Try / catch

match design_result {
    Err(e) if e.to_string().contains("describe:") => {
        let s = e.to_string();
        if s.contains("401") || s.contains("403") { /* refresh API key */ }
        else if s.contains("404") { /* endpoint changed: check VoiceStudio version */ }
        else if s.contains("429") || s.contains("50") { /* backoff and retry */ }
        else { /* surface to user */ }
    }
    other => other?,
}

Prevention

When it happens

Trigger: POST /design/describe returns 4xx/5xx: empty or overly long description text, invalid auth, unknown design endpoint, or a server error while interpreting the description.

Common situations: User submitted a blank description (opts.description empty), API token missing/expired, VoiceStudio backend updated the /design route, or rate limiting on the describe endpoint.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/e4fcc7f7349e27fd. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/voicestudio.rs:401

    pub unmatched: Vec<String>,
    pub profile_id: Option<String>,
}

/// Descrição em texto → atributos → voz nova falando o texto.
pub async fn design_speak(
    opts: DesignOptions,
    progress: super::ProgressFn,
) -> anyhow::Result<DesignResult> {
    let b = base(&opts.base_url);
    let c = client(900)?;
    super::report(&progress, "voicestudio", "describe", 0, None, None);
    let j: serde_json::Value = c
        .post(format!("{}/design/describe", b))
        .json(&serde_json::json!({ "description": opts.description }))
        .send()
        .await?
        .error_for_status()
        .map_err(|e| anyhow!("describe: {}", e))?
        .json()
        .await?;
    let instruct = j
        .get("instruct")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .to_string();
    let matched: Vec<String> = j
        .get("matched")
        .and_then(|v| v.as_array())
        .map(|a| {
            a.iter()
                .filter_map(|m| m.get("phrase").and_then(|p| p.as_str()).map(String::from))
                .collect()
        })
        .unwrap_or_default();
    let unmatched: Vec<String> = j
        .get("unmatched")

View on GitHub (pinned to 8600b91f42)