Meta Ads - Bug: `translations[]` is validated but silently discarded on the `adSetId` attach shape

POST /v1/ads/create · Meta · API v1 (spec v1.0.4) · observed 2026-08-13

Summary

On the attach shape (adSetId set), translations[] + defaultLocale pass your request validation (your distinctness validator fires on bad input — see the litmus below), the call returns 201, and the resulting Meta creative is built without the language asset_feed_spec: a plain single-language object_story_spec creative. No warning, no error — the multilingual intent is silently lost.

The identical creative surface on the standalone shape (no adSetId, no existingCampaignId) and on the composite shape (existingCampaignId) produces the correct Meta Dynamic Language Optimization creative. Verified across 18 ads on act_2191164328122008 (Meta Graph read of each ad's creative).

Expected: apply translations[] on attach

Repro A — attach shape (BROKEN)

POST /v1/ads/create (media URLs are presigned R2 GETs, truncated here; full copies available on request):

{
  "accountId": "6a7d9c5177555aae0159048e",
  "adAccountId": "act_2191164328122008",
  "name": "ZRN I18N VERIFY attach msrmz4y0",
  "adName": "ZRN I18N VERIFY attach msrmz4y0",
  "adSetId": "120253676856890536",
  "status": "PAUSED",
  "tracking": { "pixelId": "1177143927405920" },
  "headline": "Replace the Scroll",
  "body": "Still scrolling at 1am? Sutara replaces your screen with a story. Buddhist bedtime tales, narrated with rain and wind chimes. 7 minutes. Eyes closed. Your last thought tonight doesn't have to be a headline.",
  "description": "Replace the Scroll · en",
  "callToAction": "DOWNLOAD",
  "linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_EN&...",
  "video": { "url": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/en/9x16.mp4?X-Amz-..." },
  "defaultLocale": "en",
  "translations": [
    {
      "locale": "es",
      "headline": "Reemplaza el Scroll",
      "body": "¿Sigues deslizando a la 1am? Sutara reemplaza tu pantalla con una historia. Cuentos budistas para dormir, narrados con lluvia y campanillas. 7 minutos. Ojos cerrados. Tu último pensamiento esta noche no tiene que ser un titular.",
      "description": "Reemplaza el Scroll · es",
      "linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_ES&...",
      "videoUrl": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/es/9x16.mp4?X-Amz-..."
    },
    {
      "locale": "it",
      "headline": "Sostituisci lo Scroll",
      "body": "Ancora a scorrere all'1 di notte? Sutara sostituisce lo schermo con una storia. Racconti buddisti per dormire, narrati con pioggia e campanelli. 7 minuti. Occhi chiusi. Il tuo ultimo pensiero non deve essere un titolo di giornale.",
      "description": "Sostituisci lo Scroll · it",
      "linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_IT&...",
      "videoUrl": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/it/9x16.mp4?X-Amz-..."
    },
    {
      "locale": "pt",
      "headline": "Substitua o Scroll",
      "body": "Ainda rolando às 1h da manhã? Sutara substitui sua tela por uma história. Contos budistas para dormir, narrados com chuva e sinos. 7 minutos. Olhos fechados. Seu último pensamento não precisa ser uma manchete.",
      "description": "Substitua o Scroll · pt",
      "linkUrl": "https://link.sutara.app/link/d4a9b2a7-9b3d-4862-a5bd-d2168f141f70?utm-content=2_SU_PT&...",
      "videoUrl": "https://pi-agent....r2.cloudflarestorage.com/sutara/sutara/creatives/CRV-0045/v1/pt/9x16.mp4?X-Amz-..."
    }
  ]
}

Result: 201, message "Creative attached to ad set and submitted for review", ad 120253678109620536, creative 1594060508857079.

Meta Graph read of that creative (GET /1594060508857079?fields=asset_feed_spec,object_story_spec):

{
  "id": "1594060508857079",
  "object_story_spec": {
    "video_data": {
      "title": "Replace the Scroll",
      "link_description": "Replace the Scroll · en"
    }
  }
}

No asset_feed_spec. English only. All three translations gone.

Repro B — standalone shape, IDENTICAL creative surface (WORKS)

Same body with the attach addressing removed and a minimal paused tree added instead (campaignName/goal: "conversions"/optimizationGoal: "OFFSITE_CONVERSIONS"/ budgetAmount: 10/budgetType: "daily"/countries: ["US"]/ promotedObject: { "pixelId": "1177143927405920", "customEventType": "PURCHASE" } — no adSetId, no existingCampaignId). Every copy/media/translations field byte-identical to Repro A.

Result: 201, ad 120253678130730536, creative 1091962366835035.

Meta Graph read of that creative: full language feed —

{
  "id": "1091962366835035",
  "asset_feed_spec": {
    "videos": [
      { "adlabels": [{ "name": "default" }] },
      { "adlabels": [{ "name": "lang_0"  }] },
      { "adlabels": [{ "name": "lang_1"  }] },
      { "adlabels": [{ "name": "lang_2"  }] }
    ],
    "titles": [
      { "adlabels": [{ "name": "default" }], "text": "Replace the Scroll" },
      { "adlabels": [{ "name": "lang_0"  }], "text": "Reemplaza el Scroll" },
      { "adlabels": [{ "name": "lang_1"  }], "text": "Sostituisci lo Scroll" },
      { "adlabels": [{ "name": "lang_2"  }], "text": "Substitua o Scroll" }
    ],
    "asset_customization_rules": [ { "customization_spec": { "locales": [7, 23, "..."] } } ]
  }
}

(The composite shape — same surface + existingCampaignId — also produces the feed: e.g. ads 120253676889490536 → creative 1075364648357004, 120253678205210536 → creative 1931618354171673.)

Litmus: the attach shape READS translations[] before discarding it

Same attach body as Repro A but with translations[1] (it) carrying the same headline/body/description strings as translations[0] (es):

Result: 400

{
  "error": "translations[1] (locale \"it\") has the same \"headline\" as translations[0] (locale \"es\"). Meta collapses identical text into one asset, which makes the language feed fail with a misleading \"too few texts\" error — every locale needs a distinct headline.",
  "type": "invalid_request_error",
  "code": "invalid_field_value",
  "param": "translations[1].headline"
}

So the attach pipeline parses and validates translations[] — and then the creative builder ignores it. (The standalone shape returns the identical 400 on the same input.)

Impact

Every multi-ad multilingual ad set built via your documented pattern — "create the first ad via the normal shape, then attach the rest one call each" (your adSetId field docs) — ends up with a multilingual first ad and single-language siblings, with no signal to the caller. We shipped a production launch this way before catching it via Meta Graph inspection.

Ask

Apply translations[]/defaultLocale on the attach shape (preferred — the validation half already exists there), or reject them with a 400 like other unsupported attach fields. Please also update the adSetId field description either way: it currently enumerates the attach creative surface (headline/body/description/callToAction

  • imageUrl/video OR placementAssets) without saying translations is ignored.

Please authenticate to join the conversation.

Upvoters
Status

Completed

Board
💡

Feature Request

Date

29 days ago

Author

Fedir Bobylev

Subscribe to post

Get notified by email when there are changes.