Repository navigation
fix: keep the cart and the product rating alive when a dependency fails - #14
Merged
Merged
Conversation
Collaborator
Author
Terceiro commit:
|
…okie is absent updateAttachment resolved the orderForm only from the checkout.vtex.com cookie and threw when it was missing — 3,354 errors in 6 days, the storefront's largest error group. Reproduced locally on a cold session: getCart creates the orderForm and returns the Set-Cookie, but the writes that follow it in the same page load leave before that cookie is in the jar, while the client already holds a valid orderFormId in its own signal. Second load, with the cookie present: zero errors. VTEX addresses the orderForm by the `:orderFormId` path param, so the cookie is optional for the write — it governs PII masking, not addressing. The cookie still wins when present; props.orderFormId is only consulted when it is not. The throw stays. A genuinely missing id must still error: the cart queue overwrites the local cart signal with whatever the action returns, so a no-op here would wipe a real cart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012LQPsjnQxLYzAg6qx5aC2a
…down getCart re-POSTs marketingData to echo attribution VTEX has already stored on the orderForm. When a matched campaign audience injects a null marketingTag (VTEX known issue marketingtags-with-null-value-when-campaign-audience-is-matched), that echo returns 400 CHK0038 and the error bubbled out of the loader, failing the whole getCart — 143 errors in 6 days, on every page view for the affected carts, which are concentrated in a handful of poisoned orderForms. The echo is a no-op on success, so failing it loses nothing: serve the cart that was already fetched and warn. Never sanitize or strip marketingTags to "fix" this. Overwriting VTEX's marketingData drops the promotion trigger; it hurt sales and was reverted (PR deco-cx#352). The campaign tags stay intact on the server cart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012LQPsjnQxLYzAg6qx5aC2a
iOS Safari's Apple Wallet schema scanner walks the page's JSON-LD calling hasOwnProperty.call(node, "telephone") on every node, and throws when a node is null. PDPs ship `seo.description: null` — JSON.stringify keeps null and only drops undefined — so the scanner crashed on ~104 client errors a week, all iOS, all on product pages. schema.org assigns no meaning to a null-valued property, so removing them is lossless for consumers and better schema for Google. Stripped deeply: object properties and array elements alike. Falsy-but-meaningful values survive — 0, "" and false are kept, only null goes. Chained before the existing "</script>" escaping rather than replacing it: both guards apply to the same payload. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012LQPsjnQxLYzAg6qx5aC2a
marcoferreiradev
force-pushed
the
fix/cart-orderform-fallback
branch
from
September 21, 2026 15:11
dd0cafb to
14d49d0
Compare
…ews"
The ratings endpoint answers 200 with an empty body on a small share of the
calls made from our pods — about 30 per thousand product pages, flat across the
day, spread over 46 pods, and not reproducible from outside (800 ids in one
payload, concurrent pairs, idle keep-alive: all answer JSON). Its sibling
`reviews` call, same host and same Promise.all, sees it about once a week.
`JSON.parse("")` threw, the catch returned undefined, and undefined reads as
"this product has no reviews" — which the stale-while-revalidate loader then
stored as the product's answer. So one failed call blanked the rating on the
page and the aggregateRating in the JSON-LD for the whole TTL, and kept serving
that blank as stale afterwards. It was also the storefront's largest error
group, ~4,000 lines a day.
Now the ratings call reads the body as text, retries once when it comes back
empty, and throws with what the response actually carried (status,
content-length, content-type, duration) if it repeats. An empty body is not
silence: an unknown product answers `{}`, and that still returns undefined
without a retry.
Throwing is the point. A loader handler that resolves is what gets written to
the cache, so failing leaves the last good rating in place. The PDP extension
catches it and returns the page without a rating, which is the degradation the
components already render: the reviews button and sheet hide themselves and
withRating drops the block from the JSON-LD.
Verified with a stubbed fetch: empty body always -> throws after 2 calls; empty
then valid -> returns the rating on the retry; `{}` -> undefined in 1 call;
valid -> data in 1 call.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ratings and fullReview return the handled empty result again instead of throwing; the retry and the extension try/catch go with it. The loader cache TTL is 180s and every deploy clears it, so a blank rating after a failed call is short-lived, and callers (the mobile app included) keep their 200 contract. stripNulls leaves too: the Oficina PDP JSON-LD does not go through this component, and the iOS telephone error stopped before it existed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkCbBdPTvcEiJmvG3U7MfL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Duas correções de carrinho no fork, todas de erro medido em produção. Sobe junto com deco-sites/oficina-reserva#784, que já pina esta branch.
Rebaseada em
main-althoje: antes disso o pin do site revertia o deco-cx#790 (ocontent-typedas avaliações), porquedd0cafb2não descendia decdfe1bd6.1.
orderFormIddo cliente quando o cookie atrasa —vtex/actions/cart/updateAttachment.tsupdateAttachmentresolvia o carrinho só pelo cookiecheckout.vtex.com, que é HttpOnly e chega atrasado: na sessão fria ele só é setado pelogetCartque acabou de rodar, e as escritas que vêm logo atrás saem antes. 3.354 erros em 6 dias, o maior grupo da loja. A VTEX endereça o orderForm pelo path param, então o cookie é opcional para a escrita — ele continua vencendo quando existe, e oprops.orderFormIdsó entra quando falta. O throw fica: id genuinamente ausente ainda precisa falhar, senão a fila grava um carrinho vazio por cima do real.2. CHK0038 não derruba mais o carrinho inteiro —
vtex/loaders/cart.tsO
getCartre-postamarketingDatapara ecoar atribuição que a VTEX já guardou. Quando uma audiência de campanha injetamarketingTagnula (bug conhecido deles), o eco volta 400 CHK0038 e o erro subia, derrubando ogetCartinteiro — 143 erros em 6 dias, em todo pageview dos carrinhos afetados. O eco é no-op no sucesso, então falhar nele não perde nada: serve o carrinho já buscado e avisa.marketingTagspara contornar — a PR deco-cx#352 fez isso, apagou o gatilho da promoção e machucou vendas.Retirado da PR após revisão
verified-reviews/volta ao contrato domain-alt:ratingsefullReviewdevolvem o resultado tratado em vez de lançar. Devolver o vazio numa falha grava esse vazio no cache do loader, mas por pouco tempo (CACHE_MAX_AGE_S=180, e todo deploy zera o cache). Lançar quebrava o contrato 200 de quem consome, inclusive o app mobile no/live/invokedofullReview. O retry de corpo vazio também sai: nunca foi provado que a segunda tentativa resolve.stripNullsnoSeo.tsxsai: no site da Oficina não fazia nada (o JSON-LD de produto não passa por este componente), e o erro do iOS que ele mirava parou em 11/09, antes dele existir.deno fmt,deno lintedeno check **/mod.tsverdes (hook de pre-commit).🤖 Generated with Claude Code