sipeed/picoclaw · error

feishu file upload: %w

Error message

feishu file upload: %w

What it means

The file upload call Im.V1.File.Create failed at transport level before the API answered — network failure, timeout, or an unusable *os.File. The underlying error is preserved with %w. Feishu file uploads are larger than images (limit 30MB), so client timeouts are the most common cause.

Source

Thrown at pkg/channels/feishu/feishu_64.go:1205

	switch fileType {
	case "audio":
		feishuFileType = "opus"
	case "video":
		feishuFileType = "mp4"
	}

	// Upload file to get file_key
	uploadReq := larkim.NewCreateFileReqBuilder().
		Body(larkim.NewCreateFileReqBodyBuilder().
			FileType(feishuFileType).
			FileName(filename).
			File(file).
			Build()).
		Build()

	uploadResp, err := c.client.Im.V1.File.Create(ctx, uploadReq)
	if err != nil {
		return fmt.Errorf("feishu file upload: %w", err)
	}
	if !uploadResp.Success() {
		c.invalidateTokenOnAuthError(uploadResp.Code)
		return fmt.Errorf("feishu file upload api error (code=%d msg=%s)", uploadResp.Code, uploadResp.Msg)
	}
	if uploadResp.Data == nil || uploadResp.Data.FileKey == nil {
		return fmt.Errorf("feishu file upload: no file_key returned")
	}

	fileKey := *uploadResp.Data.FileKey

	// Send file message
	content, _ := json.Marshal(map[string]string{"file_key": fileKey})
	req := larkim.NewCreateMessageReqBuilder().
		ReceiveIdType(larkim.CreateMessageV1ReceiveIDTypeChatId).
		Body(larkim.NewCreateMessageReqBodyBuilder().
			ReceiveId(chatID).
			MsgType(larkim.MsgTypeFile).

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Unwrap the cause with errors.As (*url.Error, *net.OpError, *fs.PathError) to separate network from file issues.
  2. Confirm the file handle is open and seeked to offset 0 before sendFile.
  3. Raise the Lark client timeout for file uploads or compress/split files under 30MB.
  4. Verify egress/proxy from the runtime host.

Example fix

// before
err := ch.SendMedia(ctx, msg)

// after
var opErr *net.OpError
if errors.As(err, &opErr) {
    // transport-level: safe to retry the whole upload with a fresh file handle
}
var pathErr *fs.PathError
if errors.As(err, &pathErr) {
    // local file problem: reopen/rewind the file before retrying
}
Defensive patterns

Strategy: retry

Validate before calling

if _, serr := file.Stat(); serr != nil {
    return fmt.Errorf("file unusable: %w", serr)
}
if fi, _ := file.Stat(); fi.Size() > 30<<20 {
    return fmt.Errorf("file exceeds Feishu 30MB limit")
}
file.Seek(0, io.SeekStart)

Type guard

func isTransportErr(err error) bool {
    var opErr *net.OpError
    return errors.As(err, &opErr)
}

Try / catch

err := ch.SendMedia(ctx, msg)
if err != nil {
    var pathErr *fs.PathError
    if errors.As(err, &pathErr) {
        // local file problem: reopen before retrying — retry alone will not fix it
    } else if isTransportErr(err) {
        // network: safe to retry with the same handle
    }
}

Prevention

When it happens

Trigger: DNS/egress failure to open.feishu.cn; file handle nil/closed/EOF; client timeout uploading a tens-of-MB file on a slow link; ctx cancelled mid-upload.

Common situations: Sending large attachments (logs, media) over constrained uplinks; temp file deleted by cleanup while still open; pod network policy blocking egress mid-session.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/421a2fd8afe9b5c8. Report an issue: GitHub.