Tencent/WeKnora · error

failed to open file: %w

Error message

failed to open file: %w

What it means

SaveFile opens the uploaded multipart file (file.Open() on a *multipart.FileHeader) before uploading to OSS; this error wraps any failure from that open operation with 'failed to open file: %w'. It means the multipart part backing the upload could not be read — typically because the underlying temp file or request body is no longer available.

Source

Thrown at internal/application/service/file/oss.go:174

	if err != nil {
		return fmt.Errorf("failed to check OSS bucket: %w", err)
	}
	if !exists {
		return fmt.Errorf("bucket %q does not exist", s.bucketName)
	}
	return nil
}

// SaveFile saves a file to OSS using the Uploader manager for large files.
func (s *ossFileService) SaveFile(ctx context.Context,
	file *multipart.FileHeader, tenantID uint64, knowledgeID string,
) (string, error) {
	ext := filepath.Ext(file.Filename)
	objectName := fmt.Sprintf("%s%d/%s/%s%s", s.pathPrefix, 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 = utils.GetContentTypeByExt(ext)
	}

	// Use Uploader for files > 10MB (auto multipart with concurrent uploads)
	const multipartThreshold = 10 * 1024 * 1024
	if file.Size > multipartThreshold {
		uploader := s.client.NewUploader(func(uo *oss.UploaderOptions) {
			uo.PartSize = 10 * 1024 * 1024 // 10MB per part
			uo.ParallelNum = 3             // 3 concurrent uploads
		})

		_, err = uploader.UploadFrom(ctx,
			&oss.PutObjectRequest{

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Upload synchronously inside the request handler before returning — never defer SaveFile to a goroutine or background job using the same FileHeader
  2. Verify the server temp dir exists, is writable, and has free space (check TMPDIR, disk usage, permissions)
  3. Read the multipart form fully and check r.ParseMultipartForm errors before accessing the FileHeader
  4. If async processing is needed, copy the file contents into a buffer/temp file you own within the handler, then pass that

Example fix

// before (broken async)
func handler(w http.ResponseWriter, r *http.Request) {
    f, _, _ := r.FormFile("file")
    go svc.SaveFile(ctx, tenantID, kid, f) // temp file gone by the time goroutine runs
}
// after
func handler(w http.ResponseWriter, r *http.Request) {
    fh, _, err := r.FormFile("file")
    if err != nil { http.Error(w, err.Error(), 400); return }
    defer fh.Close()
    if _, err := svc.SaveFile(r.Context(), tenantID, kid, fh); err != nil {
        http.Error(w, err.Error(), 500); return
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before SaveFile, verify the FileHeader is readable in the same request lifecycle
f, err := fh.Open()
if err != nil { return fmt.Errorf("upload unreadable: %w", err) }
f.Close()
// Also check temp space
if st, err := os.Stat(os.TempDir()); err != nil || !st.IsDir() { return errors.New("temp dir unavailable") }

Try / catch

objectPath, err := svc.SaveFile(ctx, tenantID, knowledgeID, fileHeader)
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) {
        return fmt.Errorf("upload temp file inaccessible (%s): %w", pe.Path, err)
    }
    return fmt.Errorf("save failed: %w", err)
}

Prevention

When it happens

Trigger: Calling SaveFile with a *multipart.FileHeader whose underlying temp file was removed, whose request body was already consumed/closed, or a corrupted multipart form; also possible when the FileHeader was retained past the HTTP request lifetime (Go cleans request temp files when the handler returns).

Common situations: Storing multipart.FileHeader in a queue/cache and calling SaveFile after the HTTP request has ended; server temp directory (TMPDIR) permissions or disk-full preventing temp file writes; client disconnected mid-upload; middleware closing the request body early.

Related errors


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