← back to Gmc Titlefix
fix(mc-read): stop 400 on legacy bare-variant-id offers; tag non-404 as notMeasured (TK-11846)
c9c9e59f4f533fa65f9b34e98070fbdb01f2df56 · 2026-09-16 15:32:06 -0700 · Steve
toV1Name unconditionally prepended 'online~', so a legacy bare-variant feed
name (en~US~<offerId>, e.g. all 945 MDC offers) became online~en~US~<id> /
online~<id> and getProduct() returned HTTP 400 '[name] Invalid' for every one.
A caller treating that throw as 'no override present' gets a false negative on
a compliance check.
- toV1Name now: strips a full 'accounts/<mid>/products/' prefix; passes a
'~'-delimited v1 name through verbatim (legacy en~US~x AND online~en~US~x);
swaps colon v2.1 rids; defaults a bare offerId to the online feed.
- getProduct tags a non-404 failure e.notMeasured=true so a 400/401/403/429/5xx
reads as UNKNOWN (NOT_MEASURED), never as a genuine absence. Only a clean 404
is 'product absent'.
Verified live: en~US~<id> and accounts/.../products/en~US~<id> now 200; colon
form unchanged (200); bogus id -> 404 notMeasured=false. 6/6 toV1Name unit cases pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EXLAV5g4HHux54nArJfyX
Files touched
Diff
commit c9c9e59f4f533fa65f9b34e98070fbdb01f2df56
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Sep 16 15:32:06 2026 -0700
fix(mc-read): stop 400 on legacy bare-variant-id offers; tag non-404 as notMeasured (TK-11846)
toV1Name unconditionally prepended 'online~', so a legacy bare-variant feed
name (en~US~<offerId>, e.g. all 945 MDC offers) became online~en~US~<id> /
online~<id> and getProduct() returned HTTP 400 '[name] Invalid' for every one.
A caller treating that throw as 'no override present' gets a false negative on
a compliance check.
- toV1Name now: strips a full 'accounts/<mid>/products/' prefix; passes a
'~'-delimited v1 name through verbatim (legacy en~US~x AND online~en~US~x);
swaps colon v2.1 rids; defaults a bare offerId to the online feed.
- getProduct tags a non-404 failure e.notMeasured=true so a 400/401/403/429/5xx
reads as UNKNOWN (NOT_MEASURED), never as a genuine absence. Only a clean 404
is 'product absent'.
Verified live: en~US~<id> and accounts/.../products/en~US~<id> now 200; colon
form unchanged (200); bogus id -> 404 notMeasured=false. 6/6 toV1Name unit cases pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EXLAV5g4HHux54nArJfyX
---
_mc-read-v1.js | 29 +++++++++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/_mc-read-v1.js b/_mc-read-v1.js
index 6142def..cc8e830 100644
--- a/_mc-read-v1.js
+++ b/_mc-read-v1.js
@@ -15,7 +15,22 @@ const { token, MERCHANT } = require('./_auth.js');
const BASE = 'https://merchantapi.googleapis.com';
async function H() { return { Authorization: 'Bearer ' + (await token()) }; }
-const toV1Name = rid => 'online~' + String(rid).replace(/^online:/, '').replace(/:/g, '~'); // 'online:en:US:x' → 'online~en~US~x'; bare offerId also ok
+// Normalize any caller-supplied product id/name to the v1 product-name SEGMENT (the part after
+// 'accounts/<mid>/products/'). Accepts, in order: a full resource name
+// 'accounts/<mid>/products/<name>'; a '~'-delimited v1 name verbatim — the ONLINE feed
+// 'online~en~US~<offerId>' OR the LEGACY bare-variant feed 'en~US~<offerId>' (this is the TK-11846
+// fix: those legacy names must NOT get an extra 'online~' or they 400 '[name] Invalid'); a
+// colon-delimited v2.1 rid 'online:en:US:<offerId>' → 'online~en~US~<offerId>'; or a bare offerId,
+// which lacks feed/lang/country so it defaults to the online feed 'online~en~US~<offerId>' (a bare
+// id cannot address a LEGACY offer — pass that offer's full gmcName instead).
+const toV1Name = rid => {
+ let s = String(rid);
+ const m = s.match(/\/products\/(.+)$/); if (m) s = m[1]; // strip 'accounts/<mid>/products/' if a full name was passed
+ s = s.replace(/^online:/, ''); // tolerate a legacy 'online:' colon prefix
+ if (s.includes('~')) return s; // already a v1 name (legacy en~US~x OR online~en~US~x) → verbatim
+ if (s.includes(':')) return 'online~' + s.replace(/:/g, '~'); // colon v2.1 rid → online~en~US~x
+ return 'online~en~US~' + s; // bare offerId → default online feed
+};
const moneyToNum = m => (m && m.amountMicros != null) ? Number(m.amountMicros) / 1e6 : (m && m.value != null ? Number(m.value) : null);
// Map v1 productStatus.destinationStatuses (country arrays) → a v2.1-style status string for a country.
@@ -36,7 +51,17 @@ async function getProduct(rid, { country = 'US' } = {}) {
const name = `accounts/${MERCHANT}/products/${toV1Name(rid)}`;
const r = await fetch(`${BASE}/products/v1/${name}`, { headers: await H() });
const j = await r.json();
- if (!r.ok) { const e = new Error(`v1 products.get HTTP ${r.status} ${JSON.stringify(j.error || j).slice(0, 160)}`); e.status = r.status; throw e; }
+ if (!r.ok) {
+ const e = new Error(`v1 products.get HTTP ${r.status} ${JSON.stringify(j.error || j).slice(0, 160)}`);
+ e.status = r.status;
+ // NOT_MEASURED discipline (TK-11846 / CLAUDE.md TK-11431 amendment 1): ONLY a clean 404 is a
+ // genuine "product absent". A 400 (malformed name — the legacy-feed bug), 401/403 (auth), 429,
+ // or 5xx means we never actually measured this product's state — a caller MUST NOT read it as
+ // "no override present" / "not on Google". Callers that gate on presence should treat
+ // e.notMeasured === true as UNKNOWN, not as an absence.
+ e.notMeasured = r.status !== 404;
+ throw e;
+ }
const attr = j.productAttributes || {};
const ps = j.productStatus || {};
const price = moneyToNum(attr.price);
← 192552c auto-data-snapshot: 2026-09-16T15:11:20 (1 data files) — dat
·
back to Gmc Titlefix
·
auto-data-snapshot: 2026-09-16T15:43:40 (2 data files) — dat 8e63c2e →