Handling unsupported syntax

The Solver covers a growing subset of SysML v2. When a model uses something outside that subset, the Solver does not fail the whole run: it analyzes what it can, skips what it cannot, and tells you exactly what was skipped and why. Nothing is silently guessed.

A partly analyzable model

Create mixed.sysml. The flow-rate check is fully supported; the diameter-match check relies on attributes whose type nothing in the model decides:

package Pump {
    private import ScalarValues::Integer;
    part pump [1] {
        attribute flowRate : Integer = 40;
        attribute inletDiameter;
        attribute outletDiameter;
        attribute matched = inletDiameter == outletDiameter;
    }
    assert constraint flowOk { Pump::pump::flowRate <= 50 }
    assert constraint portsMatched { Pump::pump::matched }
}
syside solver check -i mixed.sysml
{
  "result": {
    "subject": "Pump",
    "expression": "assertions[2]",
    "determination": "unknown",
    "outcome": {
      "shape": "assertions",
      "assertions": [
        { "name": "Pump::flowOk", "status": "satisfied",
          "counterexample": null },
        {
          "name": "Pump::portsMatched",
          "status": "unanalyzable",
          "counterexample": null,
          "reason": "depends on 'Pump::pump::inletDiameter', ..."
        }
      ]
    },
    "causes": []
  },
  "warnings": [
    { "code": "Unimplemented", "span": null,
      "message": "'Pump::pump::inletDiameter' excluded from analysis ..." },
    { "code": "Unimplemented", "span": null,
      "message": "'Pump::pump::outletDiameter' excluded from analysis ..." }
  ],
  "exit_code": 0
}

(The reason and message strings are shortened here; the real output carries them in full.)

How to read this:

  • flowOk was checked normally and is satisfied. Unsupported syntax elsewhere in the model did not block it.

  • portsMatched is unanalyzable – neither satisfied nor violated. Its reason says which element caused it and, where the Solver can tell, what to change (here: declare a type for the two diameters).

  • Each skipped element also appears in warnings, so skipped parts of the model are visible even when every remaining assertion passes.

  • determination is unknown: the run as a whole could not decide every assertion.

An unanalyzable assertion is not a violation, so the exit code stays 0. If your CI should treat “could not check” as a failure, test for it explicitly – for example:

syside solver check -i mixed.sysml | jq -e \
    '.result.outcome.assertions | all(.status == "satisfied")'

jq -e sets the exit code to 1 unless the expression is true, failing the step when any assertion is violated or unanalyzable.

When the whole query is unsupported

If the expression you pass to evaluate --expr or check --predicate itself depends on an unanalyzable element, there is no partial answer to give. The Solver prints an error object with the same kind of reason and exits with code 2:

{
  "error": {
    "code": "Unimplemented",
    "span": null,
    "message": "the query depends on 'Battery::Cell::voltage', ..."
  },
  "exit_code": 2
}

Common causes and fixes

  • Attribute has no declared type, and its uses do not decide one (as in the example above). Declare one: attribute inletDiameter : Real;.

  • A quantity attribute is missing its quantity type. Values with units need a declaration like : MassValue; see Comparing values across units.

  • A package-level part usage without an explicit multiplicity. Write part rover [1] {...}, or see Adding assumptions for the singular-usages override.

  • The construct’s meaning is genuinely open in the specification. Some syntax the Solver refuses by default can be given a stated meaning with an override; Adding assumptions lists them.