tonhowtf/omniget · error

não achei o token anti-CSRF do Goodreads; recapture os…

Error message

não achei o token anti-CSRF do Goodreads; recapture os cookies e tente de novo

What it means

When no ready export link exists on the page (or opts.force is set), trigger_and_wait must POST to /review_porter/export/<id>, which requires the anti-CSRF token embedded in the import page. If csrf_token() fails to extract it, the export cannot be triggered and this error is returned, advising a cookie recapture.

Solutions

  1. Recapture goodreads.com cookies with the extension while logged in, then rerun.
  2. Retry shortly after — if Goodreads served an A/B or degraded page once, a fresh fetch usually includes the token.
  3. If persistent, update the csrf_token() extraction pattern to match Goodreads' current markup (check the import page's HTML for the token input/meta).
  4. Skip the forced export if a ready export link exists (run without force) — the ready path does not need the CSRF token.

Example fix

// before
let opts = Options { force: true, .. };
run(&fetcher, &opts).await?; // csrf token missing

// after: reuse existing export when possible
let opts = Options { force: false, .. };
run(&fetcher, &opts).await?;
Defensive patterns

Strategy: fallback

Validate before calling

let page = fetcher.get_text(IMPORT_URL).await?;
if csrf_token(&page).is_none() && export_link(&page).is_none() {
    prompt_cookie_recapture(); // neither a token nor an existing export is usable
}

Try / catch

match run(&fetcher, &opts).await {
    Err(e) if e.to_string().contains("anti-CSRF") => {
        // fall back to reusing existing export instead of forcing a new one
        run(&fetcher, &Options { force: false, ..opts }).await?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: csrf_token() returning None on the fetched page: the token's markup attribute changed, the page variant served doesn't include the token, or the session is semi-valid (signed in but page rendered without form token).

Common situations: Goodreads rotating or renaming the CSRF input/meta element; stale cookies producing a degraded page; locale-specific page variants omitting the token field; forced re-export on a page variant that omits the form.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/lists/goodreads.rs:264

async fn trigger_and_wait(f: &Fetcher, opts: &Options, p: &ProgressFn) -> Result<(String, u64)> {
    let page = f.get_text(IMPORT_URL).await?;
    if is_signed_out(&page) {
        return Err(anyhow!(
            "a sessão do Goodreads não está válida: capture os cookies de goodreads.com na extensão"
        ));
    }
    let uid = user_id(&page).ok_or_else(|| {
        anyhow!("não achei o id da sua conta na página de importação do Goodreads")
    })?;
    let csv_url = export_csv_url(&uid);

    // Se já existe um export pronto, aproveita: o Goodreads só deixa gerar um
    // a cada poucos dias, e gerar um novo apaga o anterior.
    let ready = !opts.force && export_link(&page).is_some();
    let started = Instant::now();
    if !ready {
        let token = csrf_token(&page).ok_or_else(|| {
            anyhow!(
                "não achei o token anti-CSRF do Goodreads; recapture os cookies e tente de novo"
            )
        })?;
        f.pace().await;
        let resp = f
            .client()
            .post(export_post_url(&uid))
            .header(reqwest::header::REFERER, IMPORT_URL)
            .header(reqwest::header::ORIGIN, "https://www.goodreads.com")
            .header(reqwest::header::ACCEPT, "*/*")
            .header("X-Requested-With", "XMLHttpRequest")
            .header("X-CSRF-Token", token)
            .header(
                reqwest::header::CONTENT_TYPE,
                "application/x-www-form-urlencoded",
            )
            .body("format=json")
            .send()

View on GitHub (pinned to 8600b91f42)