Skip to content

Handling errors ​

Every operation can fail in a few specific, named ways. Each error has a _tag, so you can handle the ones you care about and let the rest through.

ts
import { Effect } from "effect";
import { fit, prophetFittingBackendLayer } from "effect-prophet";

// A typo: the timestamp is not a full UTC ISO string.
const history = [
  { timestamp: "2025-01-01", value: 10 },
  { timestamp: "2025-01-02T00:00:00.000Z", value: 12 },
];

const message = await Effect.runPromise(
  fit(history).pipe(
    Effect.as("fitted"),
    // Every failure has a `_tag`, so you can handle each kind separately.
    Effect.catchTags({
      ObservationError: (error) =>
        Effect.succeed(`Bad row ${error.rowIndex} (${error.reason}, ${error.field})`),
      OptionsError: (error) => Effect.succeed(`Bad option (${error.reason}): ${error.message}`),
      FittingError: (error) => Effect.succeed(`Could not fit (${error.reason}): ${error.message}`),
    }),
    Effect.provide(prophetFittingBackendLayer),
  ),
);

console.log(message);
txt
Bad row 0 (invalid-shape, timestamp)

The errors you may see ​

ErrorMeaningTypical fix
Input errorsThe data or options were rejected before any fitting: a bad timestamp, a missing field, an unknown option, or an option past its limit such as mcmc.chains. Each kind of input has its own error; see below.reason says what's wrong, and the location fields and issues point to the exact row or option
FittingErrorThe model couldn't be fitted to this data. reason says why, e.g. insufficient-observations, memory-limit or sampling-limit. When a regressor's own model failed, regressor names it.Check for too few rows or all-identical timestamps; see limits
PredictionErrorA forecast couldn't be computed for a row. reason says why, e.g. simulation-limit, or non-finite-forecast when a residual overflows. When a regressor's own model failed, regressor names it.Check that future rows have every field the model needs
CountryHolidayErrorA country calendar couldn't be used. reason says why, e.g. known-coverage-restriction.See calendar errors
ModelSerializationErrorSaved model data couldn't be readMake sure it was saved by the same version
EvaluationMetricErrorA score couldn't be computed. reason says why, e.g. zero-actual when mape meets an actual near zero, zero-denominator when a custom ratio divides by zero, or non-finite when a custom score returns something other than a finite number.See actual values of zero and your own metrics
MetricScaleErrorA scaled error like mase couldn't get a scale for one round. reason is insufficient-history when the round's training rows have no pairs for the lag, zero-scale when its training values never change, or non-finite when the naive errors overflow. fold and cutoff name the round.See scaled errors
EvaluationError and friendsCross-validation, search or holdout checks failedreason and the wrapped cause explain which step

In a browser, a FittingError or PredictionError with reason: "backend-failure" and backendPhase: "load" means the engine binary couldn't be downloaded, and a CountryHolidayError with reason: "snapshot-unavailable" means a calendar file couldn't be. See Browsers and bundlers.

Input errors ​

Each kind of input has its own error, so you can catch exactly the one you expect. Every input error has:

  • reason: what's wrong, one of a fixed list for that error.
  • issues: every problem with that reason, each with a message and a path relative to the input, such as [3, "regressors", "price"] for row 3's price regressor.
  • message: a readable summary.

Errors about rows also name the first rejected row: rowIndex, the field (timestamp, value, capacity, floor, regressors or conditions) and, for a regressor or condition, its name. The other input errors have path, the first rejected location.

When the input has several kinds of problems, the error reports the first kind found. Once that's fixed, the next kind shows up.

ErrorInputreason values
ObservationErrorA history of observations, as fit, forecast, crossValidate, planRollingOrigin, searchModels, residuals, detectAnomalies, anomalies and the baseline helpers takeempty, invalid-shape, missing-capacity, inconsistent-floor, capacity-not-above-floor, bound-on-non-logistic, missing-regressor, unexpected-regressor, unrepresentable-regressor, missing-condition, unexpected-condition
PredictionInputErrorTimestamps or rows to predict, including the rows residuals and detectAnomalies build from your observationsinvalid-shape, missing-capacity, missing-floor, unexpected-floor, capacity-not-above-floor, bound-on-non-logistic, missing-regressor, unexpected-regressor, unrepresentable-regressor, missing-condition, unexpected-condition
OptionsErrorModel options, checked alone and against the history they fitinvalid-shape, invalid-value, name-collision, changepoint-out-of-range, conflicting-options, limit-exceeded
FutureTimestampsErrormakeFutureTimestamps's anchor and optionsinvalid-shape, timestamp-overflow
UncertaintyOptionsErrorSimulation options for predictUncertainty, predictWithIntervals, anomaly detection, interval cross-validation and holdout checksinvalid-shape, unsupported-output
EvaluationPlanErrorA cross-validation plan, plan summary or cross-validation controlsinvalid-shape, horizon-exceeds-history, insufficient-data-after-initial, cutoff-out-of-range, empty-assessment, summary-mismatch, limit-exceeded, timestamp-overflow
MetricOptionsErrorMetric options for performanceMetrics and comparePerformance, baseline definitions and scaledErrorMetric optionsinvalid-shape, row-count-out-of-range, window-exceeds-rows
SearchOptionsErrorA searchModels request or a makeParameterGrid gridinvalid-shape, invalid-candidate-options, invalid-base-options, invalid-mode, cutoff-mismatch, duplicate-choice, limit-exceeded
HoldoutInputErrorAn evaluateHoldout requestempty, invalid-shape, partition-not-later, limit-exceeded, unsupported-objective, receipt-mismatch, objective-dropped, baselines-need-point-metric, duplicate-baseline, unsupported-uncertainty, coverage-needs-intervals

invalid-shape means the input doesn't have the expected form, such as a wrong type, a missing or unknown key, or a number out of its allowed range. issues then lists each problem.

A few errors carry more context:

  • SearchOptionsError names the candidate with candidateIndex. When a candidate's options or the search's mode are the problem, cause holds the original OptionsError, EvaluationPlanError or UncertaintyOptionsError.
  • HoldoutInputError names the partition, development or holdout, when one is the problem, and its first rejected row in rowIndex.
  • Options for a regressor's own model are reported as an OptionsError under ["regressors", index, "predictor", ...].
  • An operation that takes several inputs reports each with its own error, with paths relative to that input. searchModels reports its plan with an EvaluationPlanError, and evaluateHoldout reports the selected options and uncertainty with an OptionsError and an UncertaintyOptionsError.
  • A search candidate that fails with failurePolicy: "record" keeps only its error's tag and reason in the result, so the result can be saved without the issues' messages.

To handle any input problem the same way, use isInputError, which matches every error above. The InputError type is their union:

ts
import { Effect } from "effect";
import { fit, isInputError, prophetFittingBackendLayer } from "effect-prophet";

const result = fit(history).pipe(
  Effect.catchIf(isInputError, (error) => Effect.succeed(`Bad input: ${error.message}`)),
  Effect.provide(prophetFittingBackendLayer),
);

Without Effect ​

If you just await Effect.runPromise(...) and an error happens, the promise rejects with that error, so a normal try/catch works:

ts
try {
  const model = await Effect.runPromise(
    fit(history).pipe(Effect.provide(prophetFittingBackendLayer)),
  );
} catch (error) {
  console.error(error);
}

Inspired by Prophet from Meta. Not affiliated with Meta.