Stirling-Tools/Stirling-PDF · error · Error
No SaaS session
Error message
No SaaS session
What it means
Plain Error('No SaaS session') thrown by invokeEdge() in procurement.ts when getSupabaseClient() returns a falsy value. invokeEdge() drives Supabase Edge Functions (issue/accept-procurement-quote) via supabase.functions.invoke. A demo/MSW response is consulted first; only when no demo matches AND there is no Supabase client does this throw.
Source
Thrown at frontend/editor/src/portal/api/procurement.ts:371
* shared project — pressing "Generate quote" in dev billed nothing but left real objects behind.
*/
async function demoEdgeResponse(
fn: string,
quoteId: number,
): Promise<Response | undefined> {
const base = saasApiBase();
if (!base) return undefined;
return resolveDemoResponse(new URL(`${base}/functions/v1/${fn}`), {
method: "POST",
body: { quote_id: quoteId },
});
}
async function invokeEdge<T>(fn: string, quoteId: number): Promise<T> {
const demo = await demoEdgeResponse(fn, quoteId);
if (demo) return (await demo.json()) as T;
const supabase = getSupabaseClient();
if (!supabase) throw new Error("No SaaS session");
const { data, error } = await supabase.functions.invoke<T>(fn, {
body: { quote_id: quoteId },
});
if (error) throw error;
if (data == null) throw new Error(`${fn} returned no data`);
return data;
}
/** Turn a draft into an issued Stripe Quote (finalized → gets a number + PDF, shareable). */
export function issueQuote(quoteId: number): Promise<QuoteResult> {
return invokeEdge<QuoteResult>("issue-procurement-quote", quoteId);
}
/** Accept an issued quote → Stripe creates the committed subscription + first invoice. */
export function acceptQuote(quoteId: number): Promise<AcceptResult> {
return invokeEdge<AcceptResult>("accept-procurement-quote", quoteId);
}
View on GitHub (pinned to 9ef20dcab8)
Solutions
- Ensure the Supabase client is initialised (SaaS auth bootstrap) before procurement actions are reachable — gate the quote UI on session presence.
- Catch the error and route the user to SaaS login/link.
- In tests, mock getSupabaseClient to return a stub client (the existing test pattern).
- Replace the plain Error with SaasNotLinkedError for consistency with http.ts so callers handle one error type.
Example fix
// before — plain Error, inconsistent with http.ts SaasNotLinkedError
const supabase = getSupabaseClient();
if (!supabase) throw new Error("No SaaS session");
// after — reuse the named error type so UI handling is uniform
import { SaasNotLinkedError } from "@portal/api/http";
const supabase = getSupabaseClient();
if (!supabase) throw new SaasNotLinkedError(); Defensive patterns
Strategy: validation
Validate before calling
import { getSupabaseClient } from "@app/auth/supabase/supabaseClient";
function hasSaasSession(): boolean { return getSupabaseClient() != null; }
// before quote actions: if (!hasSaasSession()) promptSaasLogin(); Type guard
function isNoSaasSession(e: unknown): boolean {
return e instanceof Error && e.message === "No SaaS session";
} Try / catch
try { return await issueQuote(quoteId); }
catch (e) { if (isNoSaasSession(e)) { navigate('/portal/link'); return; } throw e; } Prevention
- Gate procurement quote actions on getSupabaseClient() presence.
- Ensure SaaS auth bootstrap runs before procurement views mount.
- Unify on SaasNotLinkedError for consistent caller handling across the portal.
When it happens
Trigger: issueQuote() or acceptQuote() called when the Supabase client is not initialised (SaaS auth not bootstrapped, or running in a flavour/context that never created the client), and no demo handler intercepts the edge-function URL.
Common situations: Calling procurement quote actions in a self-hosted/non-SaaS context where the Supabase client was never created; the auth bootstrap that initialises the client has not run yet; a test forgot to mock getSupabaseClient.
Related errors
- Sign up failed
- No SaaS session — admin must link an account before attended
- Timed out waiting for anonymous session token
- error.message
- Unknown error occurred during signup
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/b1407fe4c81dd2b0.
Report an issue: GitHub.