Skip to main content
Kardinal doesn’t reject an infeasible plan outright — see Hard vs soft constraints for why. The API still returns 200 with a solution; infeasibility shows up as specific stops or resources being left out of it. This guide covers how to recognize that and work back to the cause.

How to recognize an infeasibility response

There is no dedicated “infeasible” status — check these fields on the solution instead:
  • unaffectedStopIds — non-empty means at least one stop could not be planned within the hard constraints. This is the most common signal and the one to check first.
  • unusedResourceIds — non-empty means at least one resource was left with nothing assigned. This isn’t necessarily a problem (it can be minimizeResources doing its job) — see the third bullet under “Common causes” below before assuming it’s a bug.
  • tours[].isValid: false — a specific tour violates a hard constraint. This is rarer in practice (the engine generally avoids constructing an invalid tour rather than returning one), but check it per-tour rather than assuming the plan is fine because the top-level call succeeded.
If none of these are populated, the plan is fully feasible as submitted — a “worse than expected” result (too much delay, an unbalanced fleet) is a soft-constraint or objective-ordering question, not an infeasibility one; see How the optimization engine works instead.

Diagnostic method: which constraint is at fault

For each stop in unaffectedStopIds, walk through the hard constraints in Hard vs soft constraints and check them against that stop and the resources that could plausibly serve it:
  1. Time windows. Is there any resource whose workingTimeWindow overlaps at least one of the stop’s authorizedTimeWindows, once travel time to/from the stop is accounted for? A window that’s technically non-empty but unreachable given travel time is the single most common cause — especially if the window came from a preferredTimeWindows field that was accidentally modeled as authorizedTimeWindows (see Hard vs soft constraints); a window that’s a hard constraint by mistake turns a should-be-late delivery into a dropped one.
  2. Skills. Does any available resource’s skills array cover every one of the stop’s order’s requiredSkills? A single missing skill on every resource is enough to make the stop unserviceable.
  3. Capacities. Does any resource declare every capacity key the stop consumes, with enough remaining headroom at that point in a plausible tour? Remember capacities are free-form — a typo in a key name (weight vs Weight) silently makes the stop unmatchable to every resource, with no error raised.
  4. Order structure. If the order has successiveStops or a maxStopSpan, check whether the timing implied by other constraints (time windows, breaks) makes that structural requirement impossible to satisfy alongside them.
  5. Resource-level bounds. Would serving this stop push a resource over its maxWorkingDuration, maxDistanceInKm, or maxInterStopDistanceInKm / maxInterStopDuration?
Work through these in order — time windows and skills are the fastest to rule in or out and cause the large majority of real-world infeasibility.

Resolution strategies

Once you’ve identified the binding constraint, the fix is usually one of:
  • Relax the constraint, if it was set stricter than the business actually requires — the most common example is switching a contractual delivery window from authorizedTimeWindows to preferredTimeWindows once you confirm a late visit is acceptable.
  • Add or reassign a resource — a stop with no resource combination that satisfies its skills/capacity/time-window requirements simply needs one that does; this is a fleet-sizing problem more than a modeling one.
  • Lower the stop’s priority or mark it "optional": true if it’s acceptable for it to be dropped under pressure from higher-priority stops — this doesn’t fix infeasibility, but it makes the trade-off explicit and intentional instead of an unplanned side effect.
  • Check additionalConstraints/globalConstraints (incompatibleStopTags, forbiddenAssignment, maxStopTagGroups, maxCumulatedCost, etc.) for anything scoped to the stop’s or resource’s tags — these are easy to forget once a plan has grown past its first few constraints.

See also

  • Hard vs soft constraints — the conceptual explanation of which fields are walls and which are targets, referenced throughout the diagnostic method above.
  • Data model — full field reference for everything checked in the diagnostic steps.
  • Modeling advanced constraints — worked examples for capacities, skills, breaks, and multiple time windows.