chenhg5/cc-connect · error

first-chunk: unexpected status %d

Error message

first-chunk: unexpected status %d

What it means

Thrown when the first-chunk probe receives any HTTP status other than 200 or 206 — e.g. 401/403 (auth rejected), 404 (message or file_key gone), or 5xx. The chunked downloader only understands those two success shapes and refuses to interpret anything else, giving the raw status code for diagnosis. The caller then falls back to resourceSingleGet, which reports a fuller status+body error if it also fails.

Source

Thrown at platform/feishu/resource_download.go:181

			return nil, 0, fmt.Errorf("read first-chunk body: %w", err)
		}
		return body, total, nil

	case http.StatusOK:
		// Server ignored Range and sent the full body. We deliberately
		// honour it and skip the chunked loop — this matches pre-#1741
		// behaviour for files small enough that Feishu doesn't truncate.
		body, err := io.ReadAll(io.LimitReader(resp.Body, p.resourceMaxBytes+1))
		if err != nil {
			return nil, 0, fmt.Errorf("read full body: %w", err)
		}
		if int64(len(body)) > p.resourceMaxBytes {
			return nil, 0, fmt.Errorf("resource too large: body exceeds cap %d", p.resourceMaxBytes)
		}
		return body, 0, nil

	default:
		return nil, 0, fmt.Errorf("first-chunk: unexpected status %d", resp.StatusCode)
	}
}

// resourceFetchRemainingChunks loops Range GETs starting after the first
// byte, concatenates them with `first`, and verifies the total size matches
// what the probe advertised.
func (p *Platform) resourceFetchRemainingChunks(ctx context.Context, token, messageID, fileKey, resType string, total int64, first []byte) ([]byte, error) {
	buf := bytes.NewBuffer(make([]byte, 0, total))
	buf.Write(first)

	chunkSize := p.resourceChunkSize
	if chunkSize > resourceRangeMaxRangeHeaderBytes {
		chunkSize = resourceRangeMaxRangeHeaderBytes
	}
	chunks := 1 // count the first byte we already have
	for offset := int64(1); offset < total; offset += chunkSize {
		end := offset + chunkSize - 1
		if end >= total {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the fallback resourceSingleGet error, which includes a body snippet with Feishu's code/msg
  2. On 401/403: re-check app permissions (im:resource) and tenant token minting; the next download remints the token
  3. On 404: the message or file_key no longer exists — treat as unrecoverable and inform the user
  4. On 5xx: retry later; check Feishu status/uptime

Example fix

null
Defensive patterns

Strategy: fallback

Validate before calling

null

Type guard

null

Try / catch

if err != nil && strings.Contains(err.Error(), "unexpected status") {
    var apiErr *feishu.APIError
    if errors.As(err, &apiErr) && apiErr.Code == 99991672 { /* 404: file gone */ }
}

Prevention

When it happens

Trigger: resourceDownloadHTTP returns 3xx/4xx/5xx on the Range bytes=0-0 GET: expired tenant token mid-flight, wrong messageID/fileKey, resource already deleted, or Feishu server error.

Common situations: Bot's app removed from the chat (403), user deletes the message/file before the bot downloads it (404), token cache expiry race (401), Feishu incident (5xx).

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 chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/d77cdd39b5d68f00. Report an issue: GitHub.