Tencent/WeKnora · error

failed to open file: %w

Error message

failed to open file: %w

What it means

SaveFile wraps the error from file.Open() (multipart.FileHeader.Open) when the uploaded file handle cannot be opened. This happens after the multipart form was parsed but before any OBS upload, so nothing was written to the bucket.

Source

Thrown at internal/application/service/file/obs.go:164

	}
	return "obs://"
}

func (s *obsFileService) SaveFile(ctx context.Context,
	file *multipart.FileHeader, tenantID uint64, knowledgeID string,
) (string, error) {
	ext := filepath.Ext(file.Filename)

	var objectKey string
	if s.pathPrefix != "" {
		objectKey = fmt.Sprintf("%s/%d/%s/%s%s", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)
	} else {
		objectKey = fmt.Sprintf("%d/%s/%s%s", tenantID, knowledgeID, uuid.New().String(), ext)
	}

	src, err := file.Open()
	if err != nil {
		return "", fmt.Errorf("failed to open file: %w", err)
	}
	defer src.Close()

	contentType := file.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	_, err = s.client.PutObject(ctx, &s3.PutObjectInput{
		Bucket:        aws.String(s.bucketName),
		Key:           aws.String(objectKey),
		Body:          src,
		ContentLength: aws.Int64(file.Size),
		ContentType:   aws.String(contentType),
		// ACL:           "private",
	})
	if err != nil {
		return "", fmt.Errorf("failed to upload file to OBS: %w", err)

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Inspect the wrapped error (%w) — if it is os.ErrNotExist, the multipart temp file vanished; re-upload instead of retrying Open
  2. Check the server's temp directory exists, is writable, and is not aggressively cleaned during requests
  3. Ensure the client request completes fully (timeouts, Content-Length) so the multipart part is intact
  4. Return 4xx/5xx to the client prompting re-upload rather than retrying server-side

Example fix

// before
src, err := file.Open()
if err != nil { return "", fmt.Errorf("failed to open file: %w", err) }
// after
src, err := file.Open()
if err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        return "", fmt.Errorf("upload temp file lost, re-upload required: %w", err)
    }
    return "", fmt.Errorf("failed to open file: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if file == nil || file.Size == 0 {
    return errors.New("empty or missing upload part")
}

Try / catch

path, err := svc.SaveFile(ctx, tenantID, kid, fh)
if err != nil {
    if errors.Is(err, fs.ErrNotExist) || strings.Contains(err.Error(), "failed to open file") {
        http.Error(w, "upload corrupted, please retry", http.StatusBadRequest)
        return
    }
    http.Error(w, "upload failed", http.StatusInternalServerError)
}

Prevention

When it happens

Trigger: Calling SaveFile with a *multipart.FileHeader whose underlying reader cannot be opened — typically a temp file that was removed, or a corrupted multipart part in the HTTP request.

Common situations: Server tmp dir cleaned mid-request (systemd-tmpfiles, /tmp cleanup); very large uploads spilling to disk then failing to reopen; request body truncated/timed out before Open.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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