sipeed/picoclaw · error

wecom upload requires 1-%d chunks, got %d

Error message

wecom upload requires 1-%d chunks, got %d

What it means

Defensive invariant in the WeCom chunked uploader: the file must produce between 1 and 100 chunks of 512KB (wecomUploadChunkMaxBytes = 512<<10, wecomUploadMaxChunks = 100). In practice this branch is unreachable, because outboundWeComMediaKind() already rejected files over 20MB (<= 40 chunks) and files under 5 bytes (which would yield 0 chunks) before the chunk math runs. Seeing it means the upstream kind-classification guard was bypassed or the constants were changed.

Source

Thrown at pkg/channels/wecom/media.go:692

) (*wecomOutboundMedia, error) {
	_ = ctx

	contentType = detectLocalWeComContentType(localPath, contentType)
	filename = ensureWeComOutboundFilename(filename, localPath, contentType)

	data, err := os.ReadFile(localPath)
	if err != nil {
		return nil, fmt.Errorf("read media file: %w", err)
	}
	size := int64(len(data))
	kind := outboundWeComMediaKind(part.Type, filename, contentType, size)
	if kind == "" {
		return nil, fmt.Errorf("unsupported wecom media type or size for %q", filename)
	}

	totalChunks := (len(data) + wecomUploadChunkMaxBytes - 1) / wecomUploadChunkMaxBytes
	if totalChunks <= 0 || totalChunks > wecomUploadMaxChunks {
		return nil, fmt.Errorf("wecom upload requires 1-%d chunks, got %d", wecomUploadMaxChunks, totalChunks)
	}

	sum := md5.Sum(data)
	initEnv, err := c.sendCommandAck(wecomCommand{
		Cmd:     wecomCmdUploadMediaInit,
		Headers: wecomHeaders{ReqID: randomID(10)},
		Body: wecomUploadMediaInitBody{
			Type:        kind,
			Filename:    filename,
			TotalSize:   size,
			TotalChunks: totalChunks,
			MD5:         hex.EncodeToString(sum[:]),
		},
	}, wecomUploadTimeout)
	if err != nil {
		return nil, err
	}
	initResp, err := decodeWeComEnvelopeBody[wecomUploadMediaInitResponse](initEnv)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. If you modified the size constants, scale wecomUploadMaxChunks accordingly (chunks = ceil(size/512KB)) or split the payload into multiple messages
  2. Otherwise treat this as a code-path bug: verify outboundWeComMediaKind ran before the chunk computation and file the invariant mismatch
  3. Reduce the attachment below the 20MB outbound cap so the normal classification path applies

Example fix

// before (constant drift)
wecomOutboundMediaMaxBytes = 60 << 20 // raised, but chunks still capped at 100 -> error 661

// after
wecomOutboundMediaMaxBytes = 60 << 20
wecomUploadMaxChunks = 128 // 60MB / 512KB = 120 chunks, give headroom
Defensive patterns

Strategy: validation

Validate before calling

// guard the invariant yourself if you tweak upload constants
const chunk = 512 << 10
func chunkCountOK(size int64) bool {
    n := (size + chunk - 1) / chunk
    return n >= 1 && n <= 100
}

if !chunkCountOK(info.Size()) { /* split or reject before send */ }

Type guard

func isWecomChunkOverflow(err error) bool {
    return err != nil && strings.Contains(err.Error(), "wecom upload requires 1-")
}

Try / catch

if err := ch.Send(msg); err != nil {
    if isWecomChunkOverflow(err) {
        // configuration drift: re-check upload constants, then split the payload
    }
}

Prevention

When it happens

Trigger: Only reachable if wecomOutboundMediaMaxBytes is raised above 100*512KB (~51.2MB) or the order of the kind check and chunk check is changed in uploadOutboundMedia; with current constants, len(data) is always 5..20,000,000, i.e. 1..40 chunks.

Common situations: A developer edits the upload limits in pkg/channels/wecom/media.go (e.g. raising the 20MB cap) without scaling wecomUploadMaxChunks; a fork adds a new media kind that skips the size guard; tests exercise the uploader with synthetic sizes past 50MB.

Related errors


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