bundleUnavailable: Silent Bundle Drop Read as 'No Discount'
This article may contain affiliate links. Its content is not affected by advertising.
In short
When a bundled placement failed to get added, the confirmation screen said only 'the discount was not applied' — never that the paired placement itself had not been submitted at all.
Conclusion
In a feature that lets an applicant bundle multiple ad placements into one application, when the paired placement dropped out of the offer — already held, already full, or a database error — the completion message said only “the discount was not applied,” never that the paired placement itself had never been added to the application. An applicant who had checked the “add the paired placement too” box was left believing it had gone through, just without the discount — when in fact nothing had been added at all.
The failure mode
The application form has a checkbox: “also apply for the paired placement in this set.” Checking it triggers a server-side recomputation of the eligible placement (getQualifierBundleOffer), which bundles in whichever placements in the set aren’t already held.
const offer = await getQualifierBundleOffer(admin, loopId, result.companyId);
for (const m of offer?.members ?? []) {
const r = await book(m.loopId);
if (r.ok) bundleAdded.push(m.label);
else bundleFailed.push(m.label);
}
When offer is null — meaning no paired placement was offered at all — this for loop never runs. Both bundleAdded and bundleFailed stay empty, and the confirmation message was built by looking at nothing but those two arrays plus whether the discount applied:
const bundleNote = !bundleRequested
? ""
: [
bundleAdded.length > 0 ? `We've also accepted your application for "${bundleAdded.join('", "')}".` : "",
bundleFailed.length > 0 ? `"${bundleFailed.join('", "')}" could not be accepted (already full, etc.).` : "",
bundleDiscountApplied
? "The bundle discount has been applied to your final charge."
: "The bundle discount conditions were not met, so no discount was applied this time. Our team will follow up about the amount.",
].filter(Boolean).join("");
When offer is null, all four conditional strings resolve empty except the last one, so “no discount was applied” is the only line left on screen. To the applicant who checked the box, that reads as “the paired placement went through, it just didn’t get the discount” — when actually the paired placement was never submitted at all.
Cause
getQualifierBundleOffer returning null was a single return value standing in for several distinct states: the paired placement already held, already full, the set not yet complete, or — critically — the query to fetch the candidate placements failing outright.
const { data: loops } = await admin
.from("placement_loops")
.select(/* ... */)
.in("id", toOffer);
This destructure never captured error. If the query failed, loops simply stayed undefined and execution continued, ultimately returning null regardless. The caller in actions.ts had no way to tell “there was genuinely nothing to offer” apart from “there was something to offer, but we failed to find out.”
The message-building code made it worse by never looking at offer itself — only at the indirect fact that bundleAdded and bundleFailed both happened to be empty.
The fix
The fix touches two files.
In actions.ts, whether an offer existed at all is now tracked directly in a bundleUnavailable flag, and a dropped offer gets an explicit line at the front of the message:
const offer = await getQualifierBundleOffer(admin, loopId, result.companyId);
bundleUnavailable = !offer || offer.members.length === 0;
for (const m of offer?.members ?? []) {
const r = await book(m.loopId);
if (r.ok) bundleAdded.push(m.label);
else bundleFailed.push(m.label);
}
const bundleNote = !bundleRequested
? ""
: [
bundleUnavailable
? "Note: the paired placement you requested could not be added — it is already under contract or currently unavailable."
: "",
// ...bundleAdded / bundleFailed / bundleDiscountApplied unchanged
].filter(Boolean).join("");
In promo-bundle.ts, the query now captures its error and logs it instead of swallowing it silently:
const { data: loops, error: loopsErr } = await admin
.from("placement_loops")
.select(/* ... */)
.in("id", toOffer);
if (loopsErr) {
console.error("[getQualifierBundleOffer] failed to fetch candidate placements", loopsErr.message);
return null;
}
The return value is still null either way, but a database failure is now at least traceable from the logs. The two-member minimum check — previously evaluated at the end of the function, after the database round-trip — was also moved ahead of the query, so an application that can never qualify for a discount no longer triggers an unnecessary database call.
Why this slipped through
This was caught via a code review finding (N1) and fixed the same day. It surfaced by enumerating every reason offer could come back null — already held, already full, set incomplete, database error — and noticing that the caller receiving that null had no way to tell which of them had actually happened.
A function that collapses several distinct failure reasons into one null (or false, or an empty array) tends to erase the distinction, from the caller’s perspective, between “nothing happened” and “something happened but there was no way to report it.” Code that builds a user-facing message needs to look at a value that directly records what actually happened on the server, not infer it from the absence of other flags.
Frequently asked questions
Q1Did this actually happen to a real applicant in production?
Unknown. This was a same-day fix from a code review finding (N1), not an incident report. The bundling feature itself was already live, so a prior applicant hitting this state can't be ruled out, but there's no record confirming it happened.
Q2Why does getQualifierBundleOffer return null?
Any of: the paired placement is already held, already full, the bundle set isn't complete, or the database query failed. Before the fix none of these were distinguished — the caller only ever saw 'null means nothing was offered.'
Q3What happened on a database error specifically?
The query against placement_loops could fail silently — no error was logged, and the result was treated the same as a legitimate zero-row response, returning null either way. There was no path to notice the failure had happened at all.
Q4What changed in the fix?
Two fixes: actions.ts now adds an explicit 'paired placement not added' sentence when the offer is dropped, and promo-bundle.ts logs query failures via console.error instead of swallowing them. The two-member check also moved ahead of the DB query.
Environment verified
- Next.js 16.2.7 / @supabase/supabase-js 2.106.2 — kimiteras-portal
- Found 2026-07-24 in code review (N1), fixed same day
What this article is based on
- TypeScript file lines 174-192commit 8d1878b
- TypeScript file lines 156-199commit 0cff778
- TypeScript file lines 227-232commit 8d1878b
- TypeScript file lines 229-239commit 0cff778
Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.