Skip to main content
orphan · snowflake

Internal stages holding files no pipeline has consumed

resource types
1
rule IDs covered
1
severity
low

What does ZopNight detect here?

An internal Snowflake stage with no COPY INTO activity in 30 days is holding staged files that no pipeline is consuming, at full storage rates. Note the honest limit: the per-stage bytes metric is currently a no-op, so this fires on copy inactivity alone.

Signal and threshold

How ZopNight evaluates Internal stages holding files no pipeline has consumed.
Field Value
Rule IDssnowflake_stage_orphaned_files
Categoryorphan
Severitylow
Metricnone — pure configuration read
Sourcesignals/orphan/snowflake-stage-purge.yaml

What this rule can and cannot see

The intent is to find stages accumulating bytes with no pipeline consuming them. The implementation is currently narrower than the intent, and it is worth being direct about that.

Snowflake’s STAGE_STORAGE_USAGE_HISTORY view does not expose per-stage bytes. It reports account-level stage storage only. So the bytes half of the signal is a no-op today. The rule fires purely on “no COPY INTO in 30 days”, and the size of the stage is unknown to it.

That means a stage flagged here might hold 2 GB or 2 TB. The finding tells you a pipeline stopped consuming, not how much it is costing you. Closing that gap needs a per-stage LIST-driven discovery path, which does not exist yet.

Why files accumulate

COPY INTO does not remove source files by default. Unless PURGE = TRUE is set, every file ever loaded stays in the stage indefinitely, billed as Snowflake storage.

A pipeline that has run daily for two years without PURGE has two years of loaded files sitting in the stage, all of them already ingested and none of them ever read again.

The other common case is a decommissioned pipeline: the producer keeps writing files to the stage, the consumer was turned off, and nothing signals the mismatch.

Running LIST on the stage, then COPY_HISTORY

Terminal window
LIST @my_stage;
SELECT stage_name, last_load_time, row_count
FROM snowflake.account_usage.copy_history
WHERE last_load_time > DATEADD(day,-30,CURRENT_TIMESTAMP())
ORDER BY last_load_time DESC;

LIST is the only way to see per-stage contents, which is exactly why the automated bytes signal is not available.

Fixing it

Set PURGE = TRUE on the COPY INTO so future loads clean up after themselves, then remove the historical backlog with REMOVE @my_stage/path. Confirm the files have genuinely been loaded before removing anything. COPY_HISTORY is the record that tells you.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·