chenhg5/cc-connect · error

%s: upload audio: %w

Error message

%s: upload audio: %w

What it means

This wraps an SDK-level failure from client.Im.File.Create, the Feishu upload API used to push the audio file before it can be referenced by a voice message. The upload runs inside withFreshTenantAccessTokenRetry (inside transient retry), so seeing this error means the upload failed even after retries with a freshly issued tenant_access_token.

Source

Thrown at platform/feishu/feishu.go:5492

		}
		audio = converted
		format = "opus"
	}

	var uploadResp *larkim.CreateFileResp
	if err := p.withTransientRetry(ctx, "upload audio", func() error {
		return p.withFreshTenantAccessTokenRetry(ctx, "upload audio", func(client *lark.Client, options ...larkcore.RequestOptionFunc) error {
			req := larkim.NewCreateFileReqBuilder().
				Body(larkim.NewCreateFileReqBodyBuilder().
					FileType(larkim.FileTypeOpus).
					FileName("tts_audio.opus").
					File(bytes.NewReader(audio)).
					Build()).
				Build()
			var err error
			uploadResp, err = client.Im.File.Create(ctx, req, options...)
			if err != nil {
				return fmt.Errorf("%s: upload audio: %w", p.tag(), err)
			}
			if !uploadResp.Success() {
				return fmt.Errorf("%s: upload audio code=%d msg=%s", p.tag(), uploadResp.Code, uploadResp.Msg)
			}
			return nil
		})
	}); err != nil {
		return err
	}
	if uploadResp.Data == nil || uploadResp.Data.FileKey == nil {
		return fmt.Errorf("%s: upload audio: no file_key returned", p.tag())
	}
	fileKey := *uploadResp.Data.FileKey

	slog.Debug(p.tag()+": audio uploaded", "file_key", fileKey, "format", format, "size", len(audio))

	audioMsg := larkim.MessageAudio{FileKey: fileKey}
	audioContent, err := audioMsg.String()

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the unwrapped cause to distinguish network vs auth vs cancellation
  2. Verify tenant_access_token acquisition works (check app_id/app_secret)
  3. Retry later — transient-retry already ran, so the failure is likely persistent
  4. Reduce audio size/duration to shrink the upload

Example fix

err := p.SendAudio(ctx, rc, audio, "opus")
if err != nil {
    slog.Error("feishu audio upload failed after retries", "err", err)
    // fall back to sending a text notice instead
    p.Reply(ctx, rc, "(voice message failed to upload)")
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-check token acquisition
// ensure ctx has sufficient deadline for large uploads
if d, ok := ctx.Deadline(); ok && time.Until(d) < 30*time.Second { /* extend deadline */ }

Try / catch

err := p.SendAudio(ctx, rc, audio, "opus")
if err != nil {
    if errors.Is(err, context.Canceled) { return err } // don't retry cancellations
    time.Sleep(backoff); err = p.SendAudio(ctx, rc, audio, "opus")
}

Prevention

When it happens

Trigger: client.Im.File.Create returns a non-nil err after retries: network failure, token acquisition failure, ctx cancellation mid-upload (large files on slow links), or multipart request build failure.

Common situations: Large audio files timing out on constrained networks; Feishu open platform outage; app credentials revoked so fresh token fetch fails inside the retry wrapper; proxy blocking multipart uploads.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/fe8e03728f1cf1ca. Report an issue: GitHub.