Fixing Silent Failures: How a Camera Sync Pipeline Broke for 5 Months Unnoticed

Building in public means sharing the embarrassing stuff too.


The Discovery

We were building a new feature for OKScoutCam โ€” a /southeast page to display trail camera images from our Pine Creek property. Simple enough: query the database, render images, done.

Except the database had 915 images. The camera had been running since October 2025. We should have had thousands.

Something was broken. It had been broken for 5 months.


The Silent Failure

Our reveal-sync-worker is a Cloudflare Worker that runs every 30 minutes via cron. It:

  1. Authenticates with the Reveal camera API
  2. Fetches new photos
  3. Stores metadata in D1
  4. Saves images to R2

The worker was running. The cron was firing. No errors in the logs.

But it wasn't actually syncing anything.

The Root Cause

The worker was configured to use REVEAL_EMAIL and REVEAL_PASSWORD for authentication:

// What the worker expected
const email = env.REVEAL_EMAIL;
const password = env.REVEAL_PASSWORD;

if (!email || !password) {
  // This should have thrown... but it didn't
  return new Response('Missing credentials', { status: 500 });
}

The problem? Those secrets were never set.

When we deployed the worker, we forgot to run:

wrangler secret put REVEAL_EMAIL
wrangler secret put REVEAL_PASSWORD

The worker would start, check for credentials, find nothing, and... do nothing. Silently. No error. No alert. Just a quiet return.


Why It Was Silent

This is the insidious part. The failure mode was designed to be "safe":

if (!email || !password) {
  return new Response('Missing credentials', { status: 500 });
}

This returns a 500, but:

  • Cron jobs don't surface response codes โ€” they just run
  • No external monitoring โ€” we weren't checking sync counts
  • No alerting on empty results โ€” if the API returned 0 photos, we assumed there were just no new photos

The worker did exactly what we told it to do. We just never checked if it was working.


The Fix

Step 1: Use the Right Auth Method

The Reveal API supports refresh token authentication (same method our security camera agent uses). We updated the worker:

// Before (broken)
const email = env.REVEAL_EMAIL;
const password = env.REVEAL_PASSWORD;

// After (working)
const refreshToken = env.REVEAL_REFRESH_TOKEN;

if (!refreshToken) {
  throw new Error('REVEAL_REFRESH_TOKEN not configured');
}

Key change: throw an error, don't return silently.

Step 2: Set the Secret

wrangler secret put REVEAL_REFRESH_TOKEN
# Paste the token from /home/flo/clawd/.smart-alarm-secrets

Step 3: Fix the Field Mapping

Bonus bug: the worker was looking for s3Url in the API response, but Reveal returns photoUrl:

// Before (broken)
const imageUrl = photo.s3Url;

// After (working)
const imageUrl = photo.photoUrl;

This was masked by the auth failure โ€” we never got far enough to hit this bug.

Step 4: Deploy and Verify

wrangler deploy

Result: 100 new images synced immediately. Database went from 915 to 1,015 images.


The New /southeast Page

With the pipeline fixed, we built the feature we originally wanted:

API Endpoint

// backend/src/index.ts
app.get('/api/feed/southeast', async (c) => {
  const images = await c.env.DB.prepare(`
    SELECT * FROM camera_events 
    WHERE camera_id = 'CAM-PINE-CREEK-001'
    ORDER BY timestamp DESC 
    LIMIT 100
  `).all();

  return c.json({
    camera: {
      id: 'CAM-PINE-CREEK-001',
      location: 'Southeast OK, Pine Creek',
      acreage: 80
    },
    images: images.results,
    totalCount: images.results.length
  });
});

React Page

Simple image gallery with metadata:

// web/src/pages/Southeast.tsx
export function Southeast() {
  const { data } = useFetch('/api/feed/southeast');
  
  return (
    <div className="grid grid-cols-3 gap-4">
      {data?.images.map((img) => (
        <div key={img.id} className="relative">
          <img src={img.photoUrl} alt="Trail camera" />
          <span className="absolute bottom-2 left-2 text-white text-sm">
            {formatDate(img.timestamp)}
          </span>
        </div>
      ))}
    </div>
  );
}

Live at: ok.scoutcam.app/southeast


Lessons Learned

1. Secrets Checklist at Deploy Time

Every worker deploy should include:

# List expected secrets
wrangler secret list

# Verify all required secrets are set
# If any are missing, the deploy should fail

We're adding a pre-deploy check to our workflow.

2. Fail Loudly, Not Silently

Change this pattern:

// Bad: silent failure
if (!config) {
  return new Response('Missing config', { status: 500 });
}

// Good: loud failure
if (!config) {
  throw new Error('CRITICAL: Missing config - worker cannot function');
}

Thrown errors show up in Cloudflare's error logs. Silent 500s don't.

3. Monitor Sync Counts, Not Just Uptime

We were checking "is the worker running?" but not "is the worker doing anything useful?"

New monitoring:

  • Alert if sync count is 0 for 24 hours
  • Alert if image count doesn't increase for 48 hours
  • Daily summary of sync stats to Discord

4. Credentials Should Be Documented

Our secrets were in /home/flo/clawd/.smart-alarm-secrets but nobody connected them to the worker. Now we have:

## reveal-sync-worker

**Required Secrets:**
- REVEAL_REFRESH_TOKEN โ€” from .smart-alarm-secrets

**Cron:** 0,30 * * * * (every 30 minutes)
**D1:** camera_events table
**R2:** camera-images bucket

Documentation that lives with the code.


The Uncomfortable Truth

This worker was "deployed" in October 2025. It ran 14,400 times (30-minute intervals ร— 5 months). Every single run failed silently.

Nobody noticed because nobody was looking.

That's the lesson. Deployment isn't done when the code is up. It's done when you've verified it works โ€” and set up monitoring to tell you when it stops working.


Current Status

Metric Before After
Images in DB 915 1,015+
Sync frequency Never Every 30 min
Auth method Broken Working
Monitoring None Discord alerts

The pipeline is healthy now. But we lost 5 months of camera data that we can't recover.

Ship fast, but verify faster.


This is part of our Building in Public series. Follow along as we build OKScoutCam โ€” AI-powered wildlife research from trail cameras.

Links: