Tencent/WeKnora · error

failed to write file: %w

Error message

failed to write file: %w

What it means

SaveBytes wraps any error from os.WriteFile when writing the byte payload to the uniquely named file under baseDir/<tenantID>/exports as "failed to write file: %w". The directory was already created successfully, so this error is specifically about creating/writing the final file, with the original OS error preserved for unwrapping.

Source

Thrown at internal/application/service/file/local.go:243

	}

	// Create storage directory with tenant ID
	dir := filepath.Join(s.baseDir, fmt.Sprintf("%d", tenantID), "exports")
	if err := os.MkdirAll(dir, 0o755); err != nil {
		logger.Errorf(ctx, "Failed to create directory: %v", err)
		return "", fmt.Errorf("failed to create directory: %w", err)
	}

	// Generate unique filename using timestamp
	ext := filepath.Ext(safeName)
	baseName := safeName[:len(safeName)-len(ext)]
	uniqueFileName := fmt.Sprintf("%s_%d%s", baseName, time.Now().UnixNano(), ext)
	filePath := filepath.Join(dir, uniqueFileName)

	// Write data to file
	if err := os.WriteFile(filePath, data, 0o644); err != nil {
		logger.Errorf(ctx, "Failed to write file: %v", err)
		return "", fmt.Errorf("failed to write file: %w", err)
	}

	logger.Infof(ctx, "Bytes data saved successfully: %s", filePath)
	relPath, _ := filepath.Rel(s.baseDir, filePath)
	return localScheme + filepath.ToSlash(relPath), nil
}

// GetFileURL returns a download URL for the file.
// When externalURL is configured, returns a presigned HTTP URL suitable for external access.
// Otherwise returns the local://... path for backward compatibility.
func (s *localFileService) GetFileURL(ctx context.Context, filePath string) (string, error) {
	// Normalize to provider:// format.
	normalized := filePath
	if !strings.HasPrefix(filePath, localScheme) {
		relPath, err := filepath.Rel(s.baseDir, filePath)
		if err != nil {
			normalized = filePath
		} else {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Unwrap with errors.Is(err, fs.ErrPermission) / syscall.ENOSPC and address accordingly: fix directory permissions or free disk/quota space
  2. Confirm the volume backing baseDir is mounted read-write and has enough free space for the payload size (df -h)
  3. Check for background cleanup/rsync jobs racing on the exports directory and exclude in-flight writes
  4. If SELinux denies writes, relabel the directory (restorecon) or adjust the container security policy
  5. Retry SaveBytes with backoff for transient ENOSPC/EIO conditions

Example fix

// before
path, err := svc.SaveBytes(ctx, data, tenantID, "report.csv", false)
if err != nil {
    log.Fatal(err) // opaque
}
// after
path, err := svc.SaveBytes(ctx, data, tenantID, "report.csv", false)
if err != nil {
    if errors.Is(err, syscall.ENOSPC) {
        // free space / alert ops, then retry
    } else if errors.Is(err, fs.ErrPermission) {
        // fix permissions on baseDir
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

func ensureFreeSpace(dir string, need uint64) error {
    var st syscall.Statfs_t
    if err := syscall.Statfs(dir, &st); err != nil {
        return err
    }
    avail := st.Bavail * uint64(st.Bsize)
    if avail < need*2 { // headroom for the payload
        return fmt.Errorf("insufficient space in %s: %d < %d", dir, avail, need*2)
    }
    return nil
}
// call before SaveBytes: ensureFreeSpace(exportsDir, uint64(len(data)))

Try / catch

path, err := svc.SaveBytes(ctx, data, tenantID, name, false)
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ENOSPC) {
        // alert / free space, then retry with backoff
    }
    return fmt.Errorf("save failed: %w", err)
}

Prevention

When it happens

Trigger: os.WriteFile(filePath, data, 0o644) fails: path component vanished between MkdirAll and WriteFile, permission denied on the directory, disk full so the write is short, file name too long after the timestamp suffix, or a symlink/race replaces the target path.

Common situations: Disk quota exceeded for the tenant volume; read-only mount discovered only at write time; baseDir on tmpfs that ran out of space for large uploads; concurrent cleanup job deleting the exports dir mid-write; SELinux/AppArmor denying write to the path.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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