Guides
Errors
Every error the runtime throws, what it carries, when it happens, and how to handle it.
All errors extend SemanticError, which extends Error. Catch the base class to mean "the semantic layer failed", or a subclass to mean something specific.
The hierarchy#
| Class | Type | Default | Description |
|---|---|---|---|
| SemanticError | extends Error | — | Base class. Every error below is an instance of it. |
| NotConfiguredError | extends SemanticError | — | No provider is configured and |
| UnsupportedByProviderError | extends SemanticError | — | The provider cannot express the request. Carries |
| LowConfidenceError | extends SemanticError | — | Measured confidence is below |
| ProviderError | extends SemanticError | — | Transport or upstream failure after retries are exhausted, a missing API key, or a malformed response. Carries |
| SemanticValidationError | extends SemanticError | — | Reserved for semantic validation ( |
When each happens#
| Situation | Error |
|---|---|
| First call, nothing configured, no key | NotConfiguredError |
choose() with more options than the provider allows, or fewer than 2 | UnsupportedByProviderError |
score() with levels outside the provider's range | UnsupportedByProviderError |
jev() has no key at request time | ProviderError |
| HTTP 4xx that is not retryable | ProviderError with status |
Retryable failures exhausted maxRetries | ProviderError with the last status |
| Timeout | ProviderError (the underlying AbortError is in the message) |
| Provider returned no answer for a key | ProviderError |
Unstable answer, minConfidence set, no fallback | LowConfidenceError |
| Invalid cache TTL string | plain Error at call time |
evaluate() with an empty output | plain Error at call time |
Handling patterns#
Fail open for review, fail closed for action#
import { SemanticError, LowConfidenceError } from "jevascript"
async function grade(ticket) {
try {
return await triage(ticket)
} catch (error) {
if (error instanceof SemanticError) {
log.warn("triage.degraded", { reason: String(error) })
return keywordFallback(ticket) // support does not stop because a model is down
}
throw error // anything else is a bug
}
}
async function canAutoReply(ticket) {
if (ticket.priority === "critical") return false
try {
return await safeToAutoReply(ticket)
} catch {
return false // when in doubt, a human answers
}
}Prefer fallback over catching LowConfidenceError#
// Instead of try/catch around LowConfidenceError…
const risk = await semantic(order).score("This order is fraudulent.", {
asProposition: true,
minConfidence: 0.9,
fallback: async () => reviewer.assess(order),
})The error is for the case where there is no sensible fallback and the caller must know.
Distinguish "unavailable" from "wrong"#
} catch (error) {
if (error instanceof ProviderError && error.status === 429) return retryLater()
if (error instanceof ProviderError) return degrade()
if (error instanceof UnsupportedByProviderError) throw error // a programming error: fix the question
}Errors inside a batch#
If the request behind a batch() fails, every promise in that batch rejects with the same error. Promise.all therefore rejects once; Promise.allSettled shows the same error under each key.
Where errors are thrown#
- Before the network:
NotConfiguredError,UnsupportedByProviderError, TTL and empty-output errors. These are programming errors and should surface in development. - On the network:
ProviderError. These are operational and should be handled. - After the answer:
LowConfidenceError. This is a policy outcome and should have a fallback.