# What Happens When You Exceed the Zakirullin Files Storage Quota: Error Handling Explained

> Exceeding Zakirullin Files storage quota aborts writes, returns fs.ErrQuotaExceeded. Learn how this error is handled for web and bot clients.

- Repository: [Artem Zakirullin/files.md](https://github.com/zakirullin/files.md)
- Tags: how-to-guide
- Published: 2026-05-21

---

**When you exceed the Zakirullin Files storage quota, the server aborts the write operation and returns the sentinel error `fs.ErrQuotaExceeded`, triggering an HTTP 413 response for web clients or a warning message for Telegram bot users.**

Zakirullin Files ([`files.md`](https://github.com/zakirullin/files.md/blob/main/files.md)) is a self-hosted markdown storage server that enforces per-user storage limits to manage disk resources. When exceeding the Zakirullin Files storage quota, the system implements a strict rejection policy that prevents any write operation from completing. This article examines the exact error flow—from the low-level filesystem checks to the final HTTP response—using the actual source code implementation.

## How Quota Enforcement Works in Zakirullin Files

The quota system operates at the filesystem abstraction layer, validating every write attempt against the user's current usage before committing bytes to disk.

### The Write Path and Quota Validation

Every file write flows through **`FS.Write`** ([`fs.go:84‑97`](https://github.com/zakirullin/files.md/blob/main/server/fs/fs.go#L84-L97)). This method first calculates the **size delta** between existing content and new content, then invokes **`checkQuota`** ([`quota.go:48‑57`](https://github.com/zakirullin/files.md/blob/main/server/fs/quota.go#L48-L57)) to validate the operation.

The `checkQuota` function compares the sum of `storageUsed` (current consumption) and the incoming `contentSize` against the per-user limit defined in `quotaKB`. If the total exceeds the configured threshold, the function immediately returns **`fs.ErrQuotaExceeded`**, a sentinel error that halts the operation.

### Error Propagation and Write Abortion

When `FS.Write` receives the `fs.ErrQuotaExceeded` error from `checkQuota`, it propagates the error unchanged to the caller. **The file is not written**, and critically, the internal quota cache remains untouched—preventing any inconsistency between the tracked usage and actual disk state. This ensures atomic rejection without side effects.

## HTTP API and Bot Error Responses

The Zakirullin Files server translates low-level filesystem errors into appropriate user-facing messages depending on the client interface.

### HTTP 413 Request Entity Too Large

For HTTP API clients, the sync endpoints catch `fs.ErrQuotaExceeded` and return **HTTP 413 (Request Entity Too Large)** with a JSON payload. Specifically:

- **`SyncFilenames`** and **`SyncFile`** in [`sync.go:159‑162`](https://github.com/zakirullin/files.md/blob/main/server/sync/sync.go#L159-L162) and [`sync.go:336‑339`](https://github.com/zakirullin/files.md/blob/main/server/sync/sync.go#L336-L339) handle text file synchronization
- **`sync_media.go:130‑133`** applies identical logic for binary media uploads

Both return the standardized response:

```json
{"error":"Storage quota exceeded"}

```

### Telegram Bot Notification

When the Telegram bot initiates a file write that exceeds the quota, the error triggers a direct user notification. In [`bot.go:309`](https://github.com/zakirullin/files.md/blob/main/server/bot.go#L309), the bot sends the explicit message:

```

Storage quota exceeded. Please delete some files.

```

This provides immediate feedback to bot users who may not see HTTP status codes.

## Unlimited Quota Bypass Configuration

Certain users may be exempt from storage limitations through explicit configuration. The helper function **`isUnlimitedQuota`** ([`quota.go:60‑75`](https://github.com/zakirullin/files.md/blob/main/server/fs/quota.go#L60-L75)) inspects `config.ServerCfg.UnlimitedQuotaIDs` to determine if a user ID qualifies for unrestricted access.

If the user is listed, **`newUserFS`** ([`fs.go:82‑92`](https://github.com/zakirullin/files.md/blob/main/server/fs/fs.go#L82-L92)) sets their quota value to `0`, which signifies *no limit*. This bypasses the `checkQuota` validation entirely, allowing unlimited writes regardless of current `storageUsed`.

## Practical Code Examples

### Detecting Quota Errors in Go

When interacting with the filesystem layer directly, handle `fs.ErrQuotaExceeded` to implement graceful degradation:

```go
fs, _ := fs.NewUserFS(userID)               // create per‑user FS
err := fs.Write(fs.DirUserRoot, "notes/Big.md", bigContent)
if errors.Is(err, fs.ErrQuotaExceeded) {
    // The user has hit the quota – react accordingly.
    fmt.Println("cannot store file: quota exceeded")
}

```

### Testing Quota Limits via HTTP API

To observe the HTTP 413 response behavior manually:

```bash

# POST a sync payload that would exceed the quota

curl -X POST https://your.server/sync \
     -H "Content-Type: application/json" \
     -d '{ "modified": [{ "path": "/notes/Big.md", "content": "...huge..." }] }' \
     -v

```

**Response:**

```

HTTP/1.1 413 Request Entity Too Large
Content-Type: application/json

{"error":"Storage quota exceeded"}

```

### Bot User Experience

For Telegram bot integrations, the user receives a plain text alert instead of an HTTP status:

```

Storage quota exceeded. Please delete some files.

```

## Summary

- **Quota validation** occurs in `checkQuota` ([`quota.go:48‑57`](https://github.com/zakirullin/files.md/blob/main/server/fs/quota.go#L48-L57)), which compares current usage plus new content size against the per-user limit.
- **Writes are atomic**: If quota is exceeded, `FS.Write` returns `fs.ErrQuotaExceeded` without modifying the filesystem or cache.
- **HTTP clients** receive **HTTP 413** with JSON `{"error":"Storage quota exceeded"}` from the sync endpoints.
- **Bot users** receive a direct Telegram message: "Storage quota exceeded. Please delete some files."
- **Unlimited users** bypass checks entirely if their ID appears in `config.ServerCfg.UnlimitedQuotaIDs`, enforced by `isUnlimitedQuota` ([`quota.go:60‑75`](https://github.com/zakirullin/files.md/blob/main/server/fs/quota.go#L60-L75)).

## Frequently Asked Questions

### What HTTP status code does Zakirullin Files return when storage quota is exceeded?

The server returns **HTTP 413 Request Entity Too Large**. This status code is generated by the sync handlers in [[`sync.go`](https://github.com/zakirullin/files.md/blob/main/sync.go)](https://github.com/zakirullin/files.md/blob/main/server/sync/sync.go) and [[`sync_media.go`](https://github.com/zakirullin/files.md/blob/main/sync_media.go)](https://github.com/zakirullin/files.md/blob/main/server/sync/sync_media.go) when they detect the `fs.ErrQuotaExceeded` error, accompanied by a JSON payload containing `{"error":"Storage quota exceeded"}`.

### Can specific users bypass the storage quota in Zakirullin Files?

Yes. Administrators can configure unlimited storage for specific users by adding their user IDs to the `UnlimitedQuotaIDs` list in the server configuration. The function `isUnlimitedQuota` ([`quota.go:60‑75`](https://github.com/zakirullin/files.md/blob/main/server/fs/quota.go#L60-L75)) checks this list, and if a match is found, `newUserFS` sets their quota to `0` (representing no limit), effectively skipping all quota validation in `checkQuota`.

### Where is the quota check implemented in the Zakirullin Files source code?

The core validation logic resides in **`checkQuota`** within [`server/fs/quota.go:48‑57`](https://github.com/zakirullin/files.md/blob/main/server/fs/quota.go#L48-L57). This function is called by **`FS.Write`** in [`server/fs/fs.go:84‑97`](https://github.com/zakirullin/files.md/blob/main/server/fs/fs.go#L84-L97) for every write operation, ensuring the check occurs at the filesystem abstraction layer before any disk I/O begins.

### Does exceeding the quota corrupt existing files in Zakirullin Files?

No. The quota check occurs *before* any file modification. If `checkQuota` returns `fs.ErrQuotaExceeded`, the error propagates up immediately and the write operation aborts without touching the filesystem. Existing files remain intact and the internal `storageUsed` cache stays consistent with the actual disk state.