Building in Public: Part 1

It Said It Worked, But It Didn't

Why AI ad management tools fail silently. Your AI reports success but targeting, budgets, and status changes never actually apply. How Google, Meta, LinkedIn, and Reddit APIs break AI-powered ad tools.

J.M.5 min read

Your AI assistant just told you it updated your ad targeting. It didn't. You're spending budget on the wrong audience and you don't know it.

AI ad management fails silently when your AI agent (whether you built it with Claude, GPT, Cursor, or any LLM) reports that it updated your targeting, paused your campaign, or changed your budget, but the ad platform quietly ignored the request. No error. No warning. You move on, and your budget keeps burning on the wrong audience. This is the most dangerous class of bug when you try to connect an LLM to Google Ads, Meta Ads, LinkedIn Ads, or Reddit Ads. It's the problem nobody warns you about when they say "just vibe-code an AI ad manager."

We've fixed 30+ bugs across 4 ad platforms building Campaign Agents. The silent failures were the ones that kept me up at night.

The Problem

When you build AI-powered ad management, every action follows the same pattern: you ask the agent to do something ("pause my LinkedIn campaign," "add interest targeting to my Meta ad set"), the agent calls a tool, the tool calls the platform API, and the tool returns a result. Simple, right?

The problem is what "returns a result" actually means across four different ad platforms.

What Actually Happened

LinkedIn: The Truthy 404

This one started as Issue #76. A user asked the agent to pause a LinkedIn campaign. The agent called update_campaign_status, got back a response object, and reported success. The campaign stayed active.

The root cause: LinkedIn's Python SDK partial_update() method returns a response object even on 404 errors. The object is truthy in Python. Our code checked if the response existed. It did. So it moved on. LinkedIn was returning RESOURCE_NOT_FOUND in the response body, but we never looked at the body.

Here's what we were doing:

# BEFORE: This passes even on a 404
response = client.partial_update(
    resource_path=campaign_urn,
    patch_set_object={"status": target_status},
    version_string=version_tag
)
return response.entity  # Truthy even when the request failed

The fix? Actually inspect what LinkedIn sends back instead of trusting the SDK's return type. Once we started checking, we found LinkedIn returns a clean 404 with RESOURCE_NOT_FOUND in the body. Before the fix, that was completely invisible to the user.

Meta: The 200 OK That Lied

Issue #103 was worse. Meta's targeting API accepts updates, returns a 200 OK, and then silently drops items it considers invalid. No error. No warning. Just... doesn't apply them.

This affected all seven of our add_meta_ads_*_targeting tools: interests, behaviors, demographics, locations, the works. A user would say "target people interested in digital marketing," the agent would call Meta's API, Meta would say "cool, done," and the interest would vanish into the void.

The reasons Meta silently drops targeting items:

  • Malformed audience IDs (even slightly wrong format)
  • Account-type mismatches (business vs. personal ad accounts)
  • Ad set in DRAFT or under review status
  • Interest categories Meta has deprecated but not removed from their docs

Our original code refetched the ad set after the write but never compared what was sent versus what came back. The fix: diff what you sent against what the platform actually stored, and surface every discrepancy.

The key insight: a 200 OK from an ad platform does not mean your change was applied. You have to read back and diff. Every. Single. Time.

Why This Is Harder Than It Looks

Every platform has its own flavor of this problem:

PlatformSilent Failure Mode
MetaReturns 200 OK, silently drops invalid flexible_spec fields
LinkedInReturns truthy response objects on 404 errors
RedditMakes targeting immutable after ad group creation. Updates appear to work but don't.
GoogleAccepts campaign-level changes on paused campaigns but doesn't apply until resume

You can't build a generic "call the API and report the result" pattern. Each platform needs its own post-mutation verification step, its own diff logic, its own understanding of what "success" actually means.

This is why "AI ad management" is a product, not a weekend project. The LLM can craft a perfectly formatted API call. But without the engineering layer that verifies, diffs, and surfaces discrepancies, your AI is confidently making up the answer.

How We Fixed It

The short answer: every mutation now gets verified. We don't trust the platform's response. We read back and confirm.

The longer answer is that this is one of 80+ verification patterns we've built into Campaign Agents, each one calibrated to a specific platform's failure mode. Meta needs diff-based field comparison. LinkedIn needs HTTP status inspection the SDK doesn't surface. Reddit needs pre-mutation immutability checks. Google needs state-aware validation.

This is the kind of work that takes months, not a weekend hackathon. And it's exactly why we built Campaign Agents as a product, so you don't have to discover each of these failure modes with your own ad budget.

The result: when a platform silently drops your changes, Campaign Agents catches it and tells you what didn't stick. No more "targeting updated successfully" when nothing actually changed.

What This Means for AI Ad Management

If you're building AI tools for ad platforms:

  • Every mutation needs verification. A success response from the API is the beginning of the process, not the end.
  • Plan for 20-50 tools per platform, not 5. Each one needs its own validation logic.
  • Silent failures are worse than errors. An error message, you can fix. A silent failure burns budget until someone notices.

The flip side is the opportunity: catching those silent problems before they burn budget is, honestly, the most valuable thing AI does in an ad account.

If you're evaluating AI ad management tools, ask this question: "What happens when a targeting update fails silently?" If the answer is "it reports success," keep looking.

Frequently Asked Questions

What are silent failures in AI ad management?
Silent failures occur when an AI assistant reports that it successfully updated your ad targeting, budget, or status, but the change was never actually applied to the ad platform. You see a success message and move on, not realizing your budget is being spent on the wrong audience.
Why is building AI ad management tools harder than connecting an LLM to an API?
Each ad platform (Google, Meta, LinkedIn, Reddit) has its own API vocabulary, hierarchy, and failure modes. Meta silently drops invalid targeting fields with a 200 OK response. LinkedIn returns truthy objects on 404 errors. You need post-mutation verification for every write. A weekend LLM wrapper can't handle the 20-50 distinct operations per platform needed for reliable management.
How do you prevent silent failures in AI ad tools?
Every mutation needs a read-back verification step. After writing to the platform API, re-fetch the entity and diff the expected state against actual state. If the platform accepted the request but silently dropped fields, surface that discrepancy immediately instead of reporting success.

Keep reading