DEV Community

Cover image for 1,958 Items Queued, 47,798 Bytes Left in My Cloud Quota
Mustafa ERBAY
Mustafa ERBAY

Posted on Originally published at mustafaerbay.com.tr

1,958 Items Queued, 47,798 Bytes Left in My Cloud Quota

Thirty-nine days ago I counted my backups on this blog and, midway through the piece,
corrected myself once. On the first pass I ran du on my iCloud folder, saw 60 kilobytes,
and wrote "sync is off." Wrong: the files had gone up to the cloud and their local copies
had been evicted, which is why they counted no blocks. I closed that parenthesis with
"what I took as proof that sync was off was proof that it was working".

Today I looked at the same folder again, this time not with du but with iCloud's own
tool. Sync is not working. My quota is full, 1,958 items are sitting in the upload queue,
my oldest file has been there for thirteen days — and nothing told me. Not Finder, not a
notification, and not the measurement in that earlier piece.

So the correction I made thirty-nine days ago was incomplete too. And while writing this
one I got it wrong a third time. I will tell that part as well, because it is what this
article is actually about.

The right tool: brctl, and the one number it gives

The macOS background service behind iCloud Drive is called bird. The command-line tool
that talks to it is brctl — there is a man page at /usr/share/man/man1/brctl.1, but
nothing in Apple's web documentation. Two of its subcommands carry the spine of this
article.

The first one asks about the quota:

$ brctl quota
47798 bytes of quota remaining in personal account
Enter fullscreen mode Exit fullscreen mode

Forty-seven thousand seven hundred ninety-eight bytes. The screenshots waiting in my queue
average 631 kibibytes, so what is left is less than a tenth of one screenshot. Running it
three times in a row returned the same number three times, so this is not a momentary
fluctuation.

Something is worth noticing here: brctl quota tells you what is left of the quota,
not what the quota is. I cannot read how much room I have from the command line — only
how much of it is gone. That asymmetry matters later: I cannot build an alarm on a fill
percentage
, only on an absolute threshold.

The queue: 1,958 items, 1.46 GiB, all on the same error code

The second subcommand is chattier. brctl status dumps every item the service considers
unclean: which folder, how many bytes, what state, when it was last attempted. On my
machine the output is 8,406 lines. Parsed, it says this:

Measurement Value
Items awaiting upload 1,958
Total pending size 1,565,198,210 bytes (1.46 GiB)
Folders involved 88
Marked up:needs-upload 1,958 (all of them)
Marked pending-quota 1,958 (all of them)
Quota remaining 47,798 bytes

Needed over remaining is 32,746. Thirty-two thousand times more data than I have room
for is standing in line.

Every record carries the same error code, CKErrorDomain:25. But the full payload appears
in only 40 of them — the ones the service actually attempted. There it looks like this:

upload_error:<NSError:(CKErrorDomain:25) - {
    CKErrorDescription = "Error uploading asset: Quota exceeded";
    CKRetryAfter = 312;
    ContainerID = "com.apple.clouddocs";
    ServerErrorDescription = "Quota exceeded";
}>
Enter fullscreen mode Exit fullscreen mode

I do not have to guess what CKErrorDomain:25 means; it is written in the CloudKit header
on my own machine. CKError.h inside MacOSX.sdk, line 171 and the comment above it:

/// An error that occurs when saving a record exceeds the user's storage quota.
/// [...]
/// **In the private database**: The user doesn't have enough iCloud storage.
/// Prompt the user to go to iCloud settings to manage their storage.
CKErrorQuotaExceeded                  = 25,
Enter fullscreen mode Exit fullscreen mode

The header even says what should happen next: prompt the user to go to iCloud settings.
That is an instruction to an app developer. In my case the app is Apple itself, and that
prompt never reached me.

A field that says "try again in five minutes"

The error payload carries a field called CKRetryAfter = 312. All forty of those records
have it; the values run between 305 and 346 seconds, median 317. Try again in roughly five
minutes, it says.

In my first draft I called this an undocumented hint, then withdrew it after reading the
same header more carefully. CKError.h says, on lines 79 and 156: "Check for a
CKErrorRetryAfterKey key in the userInfo dictionary of any CloudKit error that you
receive." So the field sitting next to a quota error is not a documentation violation —
it is exactly what the header leads you to expect.

It is still an odd sight. A field proposing a retry sits next to the one error a retry
cannot fix. Unless space frees up, a hundred attempts hit the same wall a hundred times.
And the real information is not in that field but in the counter beside it: 1,918 of the
records show attempts:0 — never tried even once. Only 40 saw a single attempt.

That counter set a trap for me, and I walked into it before climbing out. Take the
"last attempt" value across all records, compute the median, and you get 333.8 hours —
which invites the sentence "the service knocked once two weeks ago and never came back."
But that field is meaningless on 1,918 of them; an item never attempted has no last
attempt. Across the 40 records that were actually attempted, the median is 227.6 hours.
For my own 29 files it is 148.97 hours, and the most recent is just 4.33 hours before
the measurement
. The service did not turn its back. It tries each new file once as the
file appears, finds the door shut, and leaves it in the queue. 27, 28, 29, 30, 31 October;
2, 3, 4, 5, 6 and 9 November — there is an attempt on each.

Who filled the queue: I did

Of the 1,958 pending items, 1,929 — 98.5 percent — come from under a single folder. On the
morning of 26 October I ran an agent tool from the command line, and the tool opened its
workspace under ~/Documents by default:

~/Documents/<tool>/tasks/2026-10-26/11-42-03-<hash>/
Enter fullscreen mode Exit fullscreen mode

So what is inside that tree? My first guess was wrong here too. I had written "a Rust
dependency cache, downloaded source files"; counting the extensions of the queued records
produced a different table:

What Records Size
.o (compiler object files) 1,565 —
.rmeta / .rlib 152 —
.bin, .d, no extension 131 —
.rs (actual source code) 44 —
Total under target/ 1,851 1,122.3 MiB
Total under registry/src/ 51 1,232,145 bytes

This is not a download cache; it is largely build output — target/debug/deps and the
incremental compilation cache. Downloaded source accounts for 2.6 percent of the queued
records and less than a thousandth of the bytes. The difference is practical: something
downloaded gets re-downloaded, something compiled gets recompiled. Both come back, but the
second one costs CPU rather than bandwidth.

There is also this: 78 of the queued tool records (258.0 MiB) sit outside target/. Nine
of them come from a git repository's objects/pack directory, several from review-report
folders. My first draft said "not one of them is of any value to me"; I am withdrawing
that sentence, because I never looked at those 78 one by one.

Here is the annoying part: ~/Documents is a folder that goes to the cloud whenever
iCloud Drive's Desktop and Documents sync is on. Apple's documentation is explicit —
"When you turn on Desktop & Documents Folders, your Desktop folder and Documents folder
are stored in iCloud Drive," and those files "use your iCloud storage."

There is no bad intent in a tool opening its workspace there; in the Unix tradition
~/Documents is an ordinary working directory. But on my machine that directory is a
mouth opening onto the cloud, and I did not know where that mouth led.

The third mistake: "if it's not listed, it's done"

The clean version of the story was ready: the tool wrote 1.77 GiB, filled my quota, and
pushed my own files out. The data would not have it — and seeing that took me two rounds.

The first round is simple: a file whose upload fails does not consume quota. Of the
1,929 tool records in the queue, 1,918 were never attempted and 11 were attempted and
refused. Not one of them took a byte of my quota. They filled the queue; they did not fill
the quota. (None of my four sources states this; on server-side behaviour my only support
is the data itself.)

The second round is where it caught me. Looking for an answer to "then what filled the
quota?", I reasoned like this: brctl status lists only items with work pending,
therefore of the 15,433 files in the tree the 13,504 not listed must have uploaded, and
the 428 MiB difference is what flowed to the cloud that morning. I even added a note
saying I could not confirm it with a second method, because I found no command that asks
for one file's state.

Both halves were wrong.

The second method was already sitting later in this very article: whether a file reached
the cloud is told by its block count, because iCloud deletes the local copy after
uploading. Applying that same test to the tool tree:

State Files Apparent size
No blocks (uploaded, evicted) 10,279 229.1 MiB
Blocks present (still local) 5,154 1,579.3 MiB
Total 15,433 1,896,292,709 bytes (1.77 GiB)

All 1,929 queued files are necessarily on the "blocks present" side — their uploads failed,
so the local copies remain. That leaves 3,225 files that are absent from the list and
still fully on disk. So my assumption, "if it's not listed, it's done," is broken. What
actually reached the cloud is not 428 MiB but 229.1 MiB.

And the claim that no command asks for one file's state? brctl's own help text lists
dump, monitor, log and diagnose -d <document-path>; the man page has
download <path> and evict <path>. What I was looking for was in the --help output of
the tool I was writing about, two lines below where I stopped reading.

I could have deleted this and moved on. I am leaving it in, because it is precisely this
article's thesis: looking with the wrong instrument is the same thing as not noticing the
instrument is wrong.

The timing side held up. The first quota error landed 334.21 hours before the
measurement — the afternoon of 26 October — and the tool opened its workspace at 11:42
that same morning. Three and a half hours between them. There is no file of mine in
the queue modified before 26 October; the tool tree does have 60 such records, but 51 of
them are dated 2006, because vendored sources keep their upstream timestamps. Modification
time is a weak instrument here and proves nothing on its own.

Summing up: the tool did not fill my quota by itself. It consumed the last slice of a
quota already near its floor, and lined up everything after it — its own output and my
files alike — in the same dead queue.

My files: 29 of them, and mostly email

Of the 1,958 queued items, 29 are genuinely mine, 112.4 MiB in total:

Location Files Size
Desktop 13 102.85 MiB
Documents / screenshots 15 9.25 MiB
Documents root 1 0.34 MiB

Seven of the 13 Desktop files are .eml, 96.8 MiB together — the records carry a
quarantine:Download flag, so they arrived on the Desktop from a mail client. Four are
screenshots, one a PDF, one a note. The tool tree accounts for 1,447,301,235 bytes of the
queue; my 29 files are 7.5 percent of the total pending bytes.

The oldest has been waiting since 27 October at 11:24. Today is 9 November. Thirteen days.

What bothers me about this list is not the size. 112 megabytes is nothing. What bothers me
is what the list is: putting something on the Desktop is the shortest way of saying "I
want to keep this." Not build output, not a cache. Each one got its own decision to keep
it. And they are exactly what is sitting in the system's dead queue.

The indicator inverts

Now I apply that same block test to the Desktop, because the real lesson is there:

$ du -sk ~/Desktop
105664  /Users/mustafaerbay/Desktop
Enter fullscreen mode Exit fullscreen mode

103 MiB. So how much data is actually on my Desktop? Adding up apparent file sizes gives
325.4 MiB across 825 files. The ratio is 3.15, and the reason is this: 802 of the files
have no local blocks, because iCloud deleted the local copies after uploading them. Only
23 files still hold blocks on disk; their apparent size is 103.14 MiB and their block
footprint is 103.19 MiB, which is the number du reports.

And 13 of those 23 are precisely the 13 files waiting in the quota queue: 102.85 MiB. That
is 99.7 percent of the local blocks. Of the remaining 10 files, nine are dot-files and one
is an item iCloud deemed ineligible; together they are 0.3 MiB.

Turn the sentence around and you see what I was looking at: nearly every file taking up
space on my Desktop is taking up space because it could not reach the cloud.
The 103
megabytes du reports are not a health indicator, they are a failure indicator.
Thirty-nine days ago I looked at the same number and said "the data is here, sync is off,"
then corrected it to "no, no blocks means it uploaded." Both are two faces of the same
mistake. A block count on its own does not separate "uploaded" from "failed to upload."
What separates them is reading the block count together with the queue record.

There is a symmetry at the bottom of that diagram I do not like. 802 files exist only in
the cloud; 13 exist only on disk. This machine
still has no Time Machine
destination — tmutil destinationinfo said "No destinations configured" thirty-nine days
ago and says it today; tmutil listlocalsnapshots / is still empty. So no file on my
Desktop has two copies. Every one of them has one copy. Only which copy changes.

What Apple's documentation says, and doesn't

A full quota is a documented condition. Apple's iCloud storage page lists the
consequences: your device won't back up to iCloud, new photos and videos won't upload to
iCloud Photos, you can't send or receive mail at your iCloud address.

For iCloud Drive the wording is different. The page says iCloud Drive and other iCloud
apps "won't stay up to date across your devices." Won't stay up to date. Not won't
upload.

The distance between those two phrasings is my 29 files. "Not staying up to date across my
devices" sounds like a sync lag — you see a slightly older version on the iPad, then it
catches up. The measurement says something else. The file never leaves, the only copy sits
on disk, and that disk has no second copy. What is written plainly for Photos — "won't
upload" — is softened for Drive.

I am not blaming the documentation; nothing it says is technically false, and Drive really
is not staying up to date. But a user reading that page does not conclude "my emails are
on the Desktop and they are going nowhere."

What comes next

I did none of this while writing; the only thing I did today was measure. In order of how
expensive the loss would be:

  1. Copy the 29 queued files out of iCloud. There is a subtlety here: these files live in an iCloud-managed folder and Finder can show them as uploaded. brctl download <path> is the way to be sure the local copy is complete; brctl evict <path> does the opposite, which means running it on the wrong file deletes the only copy. I will not touch the source files until the copy is finished.
  2. Move agent tools' workspaces out of ~/Documents. One line of configuration, and it removes the entire 1,929-record queue. Under a folder that goes to the cloud is not the place for re-creatable build output.
  3. Either clear the quota or grow it. The free tier is 5 GB. Feeding both Desktop and Documents out of that quota and assuming 5 GB would do was my mistake.
  4. Wire brctl quota into a check. The asymmetry from the first section decides the shape here: because the command returns only the remainder, a percentage threshold is impossible, so it will be an absolute one — tell me when less than 500 MB is left.
  5. Buy a disk for Time Machine. It was item five on my list thirty-nine days ago. It is still item five.

One question I am leaving unanswered, because I did not measure it: if I delete the tool
tree, do those 1,929 records drop out of the queue on their own, or will my 29 files still
sit behind them once space frees up? I do not know the queue's ordering. I will watch it
with brctl monitor while doing item 2, and write that up separately.

Not silence — the wrong instrument

Closing the same subject thirty-nine days ago I wrote that "the default sound of systems
is silence." After today's measurement I find that sentence incomplete.

The system was not silent with me. It wrote everything down: which file, how many bytes,
which error code, how many hours since the last attempt, how much room was left. An
8,406-line report was sitting right there. I did not know the command needed to read it,
so I assumed silence.

The difference matters in practice. The fix for a silent system is to add an alert. The
fix for a system you have been looking at with the wrong instrument is to find the right
one — and as this article demonstrated three times, the wrong instrument usually returns a
reasonable answer. du always gave me a number, the number was correct every time, and it
had nothing to do with the question I was asking. "If it's not listed, it's done" was
internally consistent too; it was only wrong about what the list was.

So how do you find the right instrument? The rule I take from today's measurement: whether
a file reached the cloud is not something the file system knows. That information lives in
the ledger of the service running the sync. Blocks, sizes and folder views do not reach
that ledger, however carefully you read them. You have to ask the service itself. On my
machine that question is spelled brctl status, and I did not ask it for thirteen days.

Official Sources

Top comments (0)