Skip to content

gmc preflight

Offline feed-compliance scanner. preflight checks your product data against Merchant Center rules before you upload — catching disapprovals locally, with no API call and no auth. Run it on a feeds pull directory, a single file, or (with --remote) the live catalog. Its exit code makes it a drop-in CI gate.

sh
gmc preflight                       # scan ./feeds
gmc preflight --dir catalog         # scan a directory
gmc preflight --file product.json   # scan a single file
gmc preflight --remote              # pull the live catalog and scan it (needs auth)
gmc preflight --json                # full machine-readable report
OptionDescription
--dir <path>Directory of product files to scan (default feeds)
--file <path>Scan a single product file instead of a directory
--remotePull the live catalog and scan it (needs auth/account)
--config <path>Path to a .gmcpreflightrc (overrides discovery)
--strictTreat warnings as failures (non-zero exit)
--rule <id...>Only run the named rule(s)
--page-size <n>Max products per API page (with --remote)

Products are read as push-ready ProductInput JSON — the exact files feeds pull produces — so preflight scans precisely what push would upload. An unparseable file isn't skipped: it's reported as an error finding, because catching bad files is the point.

What it checks

Each rule has a stable dotted id and a default severity. Rules come in families: required.* (a missing attribute the Merchant API rejects) and format.* (an attribute that's present but malformed). A format.* rule fires only when its attribute is present — an absent value is the matching required.* rule's finding, so a missing title is reported once, not twice.

RuleDefaultCatches
required.offer-iderrorMissing offer id (the unique product identifier)
required.titleerrorMissing or blank title
required.descriptionerrorMissing or blank description
required.linkerrorMissing landing-page link
required.image-linkerrorMissing image_link
required.availabilityerrorMissing availability
required.priceerrorMissing price / missing amount
required.conditionwarningMissing condition (recommended; required for used/refurbished)
required.identifier-existswarningNone of gtin / mpn / brand present
format.link-urlerrorlink is not a valid http(s) URL
format.image-link-urlerrorimage_link is not a valid http(s) URL
format.price-amounterroramountMicros is not a non-negative integer count of micros
format.price-currencyerrorA priced product's currencyCode is missing or not a 3-letter code
format.availability-enumerroravailability not in in_stock / out_of_stock / preorder / backorder
format.condition-enumerrorcondition not in new / refurbished / used
format.gtin-checksumwarninggtin is the wrong length or fails its check digit
format.title-lengthwarningtitle exceeds 150 characters
format.description-lengthwarningdescription exceeds 5000 characters
policy.promotional-titleerrorPromotional text in title (e.g. "free shipping", "20% off", "best price")
policy.title-capswarningtitle is excessively capitalized (SHOUTING)
policy.title-symbolswarningGimmicky symbols or emoji in title
policy.phone-in-titlewarningA phone number in title
policy.link-httpswarningLanding-page link uses http, not https

The policy.* family predicts editorial disapproval triggers — these are heuristic, so all default to warning except policy.promotional-title (a well-known hard disapproval, an error). Override any rule's level — or turn it off — in .gmcpreflightrc; warning findings don't fail the run unless you pass --strict.

Related

gmc migrate helps you move off the Content API for Shopping (retiring Aug 18, 2026); migrated feeds drop straight into preflight.

Findings

A finding names the product (by its composite id {contentLanguage}~{feedLabel}~{offerId}, or local~{contentLanguage}~{feedLabel}~{offerId} for legacy-local products), the offending attribute, what's wrong, and how to fix it. Human output groups findings by product:

gmc preflight — scanned 2 product(s)

en~US~SKU2
  ✗ title — Missing title — every product must have a `title`.
      → Set productAttributes.title to the product's name as shown to shoppers.
  ✗ price — Missing price — every product must have a `price` with an amount.
      → Set productAttributes.price.amountMicros (and currencyCode), e.g. 49990000 / "USD".

2 errors across 1 product(s).
Failed.

--json emits the full report — ok, exitCode, scanned, strict, counts, and every finding — on a single line:

json
{
  "ok": false,
  "exitCode": 6,
  "scanned": 2,
  "strict": false,
  "counts": { "error": 2, "warning": 0, "info": 0 },
  "findings": [
    {
      "ruleId": "required.title",
      "severity": "error",
      "productKey": "en~US~SKU2",
      "offerId": "SKU2",
      "attribute": "title",
      "message": "…",
      "suggestion": "…",
      "documentation": "…"
    }
  ]
}

Configuring rules — .gmcpreflightrc

Drop a project-local .gmcpreflightrc next to your feeds (commit it — it's part of the feed-as-code workflow). preflight discovers it by walking up from the scanned directory, the same way ESLint/Prettier find their config; --config <path> overrides discovery.

json
{
  "rules": {
    "required.title": "warning",
    "required.price": "off"
  },
  "ignore": ["legacy-sku-1", "legacy-sku-2"],
  "targetCountry": "US",
  "strict": false
}
  • rules — override a rule's severity (error / warning / info) or disable it with off.
  • ignore — offer ids to skip entirely (e.g. known-legacy products); they aren't scanned or counted.
  • targetCountry — ISO-3166 alpha-2 code for locale-aware rules (used from v0.9.4).
  • strict — treat warnings as failures for the exit code. --strict on the command line forces this on.

CI gate

preflight exits non-zero when it finds gating issues, so it fails a build before a bad feed ships:

sh
gmc feeds pull --dir feeds        # export the catalog
gmc preflight --dir feeds         # exits 6 on any error-severity finding

Exit codes

0 clean (or only warnings, without --strict) · 2 usage (unknown --rule, unreadable --file/directory) · 3 auth (--remote) · 4 config (malformed .gmcpreflightrc) · 5 Merchant API (--remote) · 6 preflight found gating violations.

Released under the MIT License.