zeroclaw-labs/zeroclaw · error · anyhow::Error

upload file failed after token refresh: status={retry_status

Error message

upload file failed after token refresh: status={retry_status}, body={retry_response}

What it means

The file upload path (build_lark_file_upload_form with a file_type such as opus/mp4/pdf) hit a refresh-indicating response, refreshed the tenant token, rebuilt the multipart form, retried — and the retry still looked like an invalid-token response. Same defect class as errors 108/109 but for generic file uploads. Retry status and body are included.

Source

Thrown at crates/zeroclaw-channels/src/lark.rs:2257

    }

    async fn upload_lark_file(
        &self,
        token: &mut String,
        marker: &LarkResolvedMediaMarker,
        file_type: &'static str,
    ) -> anyhow::Result<String> {
        let url = format!("{}/im/v1/files", self.api_base());
        let form = build_lark_file_upload_form(marker, file_type).await?;
        let (status, response) = self.post_multipart_once(&url, token, form).await?;
        let response = if should_refresh_lark_tenant_token(status, &response) {
            self.invalidate_token().await;
            *token = self.get_tenant_access_token().await?;
            let retry_form = build_lark_file_upload_form(marker, file_type).await?;
            let (retry_status, retry_response) =
                self.post_multipart_once(&url, token, retry_form).await?;
            if should_refresh_lark_tenant_token(retry_status, &retry_response) {
                anyhow::bail!(
                    "upload file failed after token refresh: status={retry_status}, body={retry_response}"
                );
            }
            ensure_lark_send_success(retry_status, &retry_response, "upload file")?;
            retry_response
        } else {
            ensure_lark_send_success(status, &response, "upload file")?;
            response
        };

        response
            .pointer("/data/file_key")
            .or_else(|| response.get("file_key"))
            .and_then(|v| v.as_str())
            .map(str::to_string)
            .ok_or_else(|| anyhow::Error::msg("Lark/Feishu file upload returned no file_key"))
    }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Verify app_id/app_secret and restart (see error 108).
  2. Confirm the file upload scope for the relevant file_type is granted and the app version is published.
  3. Inspect the retry body code: non-99991663 codes indicate permission or payload issues, not tokens.
  4. Escalate to backoff-and-cooldown instead of immediate retry.
Defensive patterns

Strategy: retry

Type guard

fn is_repeat_stale_token(err: &anyhow::Error) -> bool {
    err.to_string().contains("upload file failed after token refresh")
}

Try / catch

if is_repeat_stale_token(&e) {
    cooldown(Duration::from_secs(60)).await;
    return lark.send(msg).await;
}

Prevention

When it happens

Trigger: Rotated app_secret or region-mismatched api_base, or the app missing the file-upload scope so the retry with a fresh token is still rejected.

Common situations: Credential rotation while running, duplicated channel processes, missing/withdrawn file upload permission after a permission audit.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/737c2c8896b514f6. Report an issue: GitHub.