Tencent/WeKnora · error

create form file: %w

Error message

create form file: %w

What it means

submitJob builds a multipart/form-data body and calls writer.CreateFormFile("file", filepath.Base(fileName)) to attach the uploaded document. This fails only when the multipart writer has already been closed or suffered an internal error, meaning the form body is in an invalid state. The file part could not be created, so the upload cannot proceed.

Source

Thrown at internal/infrastructure/docparser/paddleocr_vl_cloud_converter.go:131

		return "", fmt.Errorf("marshal optionalPayload: %w", err)
	}

	fileName := req.FileName
	if fileName == "" {
		ext := strings.TrimPrefix(req.FileType, ".")
		if ext == "" {
			ext = "pdf"
		}
		fileName = "document." + ext
	}

	var body bytes.Buffer
	writer := multipart.NewWriter(&body)
	_ = writer.WriteField("model", c.model)
	_ = writer.WriteField("optionalPayload", string(optional))
	part, err := writer.CreateFormFile("file", filepath.Base(fileName))
	if err != nil {
		return "", fmt.Errorf("create form file: %w", err)
	}
	if _, err := part.Write(content); err != nil {
		return "", fmt.Errorf("write file content: %w", err)
	}
	writer.Close()

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL, &body)
	if err != nil {
		return "", fmt.Errorf("create request: %w", err)
	}
	httpReq.Header.Set("Authorization", "bearer "+c.token)
	httpReq.Header.Set("Content-Type", writer.FormDataContentType())

	client := utils.NewSSRFSafeHTTPClient(utils.SSRFSafeHTTPClientConfig{Timeout: 60 * time.Second, MaxRedirects: 5})
	resp, err := client.Do(httpReq)
	if err != nil {
		return "", fmt.Errorf("HTTP request: %w", err)
	}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify field writes and CreateFormFile all happen before writer.Close()
  2. Check the wrapped error for the underlying writer state problem
  3. Ensure CreateFormFile is called exactly once per upload with a valid, non-empty filename from filepath.Base
  4. If triggered by OOM pressure on the buffer, stream to a temp file instead of an in-memory bytes.Buffer for very large documents

Example fix

// before
writer.Close()
part, err := writer.CreateFormFile("file", name) // fails: writer closed
// after
part, err := writer.CreateFormFile("file", name)
if _, err := part.Write(content); err != nil { ... }
writer.Close()
Defensive patterns

Strategy: try-catch

Validate before calling

if fileName == "" { return errors.New("file name required for multipart upload") }
if err == nil && writerClosed { return errors.New("multipart writer closed before file part created") }

Type guard

func isFormFileError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "create form file")
}

Try / catch

out, err := reader.Read(ctx, req)
if isFormFileError(err) {
    return fmt.Errorf("multipart assembly bug (check write/close ordering): %w", err)
}

Prevention

When it happens

Trigger: Read -> submitJob -> CreateFormFile returns an error — occurs when writer.Close() was called before CreateFormFile, or the multipart writer is otherwise in an error state (e.g. a prior WriteField write already poisoned it after an underlying io error on the buffer).

Common situations: Code refactors that reordered Close() before CreateFormFile; a bytes.Buffer write failure (virtually only under memory exhaustion); duplicating a part with the same field name after Close.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/437e25a7eb144770. Report an issue: GitHub.