Skip to content

Missing-parameter error reports a count, not the parameter names #20

Description

@petlenz

Summary

When required parameters are missing, input_parameter_controller::accept() throws an exception carrying only a count, not the names:

missing 1 required parameter(s)

The names are known at the throw site — they are collected in missing and printed to stdout — but they never reach the exception. A caller that catches the error, or runs anywhere stdout is not visible, is told that something is missing but not what.

Where

include/numsim-core/input_parameter_controller.h, in accept() (step 4):

    // 4. Report all missing required parameters at once
    if (!missing.empty()) {
      std::println("  missing required parameters:");
      for (const auto& key : missing)
        std::println("    - {}", key);
      throw std::invalid_argument(
          "missing " + std::to_string(missing.size()) + " required parameter(s)");
    }

Why it matters

Two paths in this header report the same condition, and they disagree. The single-parameter check already does the right thing:

  void check(ParameterHandler &input) const final override {
    if (!input.contains(this->m_para.name())) {
      throw std::invalid_argument("Parameter " + this->m_para.name() +
                                  " is missing!");
    }
  }

So the quality of the diagnostic depends on which validation path happens to run. The batch path in accept() is the one a document- or deck-driven flow goes through, which is exactly the case where the user did not write the C++ and has no other way to find out which key is wrong.

Concretely, in numsim-materials a JSON material model that omits one cone parameter of a Drucker-Prager material fails with:

missing 1 required parameter(s)

for a document that named eta, beta, K_bulk, G, sigma_0, hardening_source, strain_source and solver_source — eight candidates, one of them wrong, and the error narrows it to none of them. We have a test pinning this behaviour rather than the desirable one (MaterialRegistry.ADruckerPragerDocumentMissingEtaFailsLoudly), with a comment pointing here.

Printing to stdout does not cover the gap:

  • a library writing diagnostics to stdout is not usable from a host that owns that stream — an Abaqus UMAT, a Python binding, a GUI;
  • the print and the throw can be separated by arbitrary output from other materials being constructed;
  • a caller that catches and re-throws with context (as numsim-materials' UMAT boundary does, mapping failures to a solver return code) has nothing to add the names to.

Suggested fix

Interpolate the names that are already collected:

    if (!missing.empty()) {
      std::string names;
      for (const auto& key : missing)
        names += (names.empty() ? "" : ", ") + std::string(key);
      throw std::invalid_argument(
          "missing " + std::to_string(missing.size()) +
          " required parameter(s): " + names);
    }

Two things worth deciding at the same time:

  1. Whether the std::println calls in accept() (missing parameters, and the using default: '{}' lines above them) belong in a library at all, or should move behind a caller-supplied sink.
  2. Whether the two messages should be unified, so "Parameter eta is missing!" and the batch form read the same way.

Happy to send a PR for the message change if the shape above is what you want.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions