Parameter Validation

The Saner Python SDK includes a built-in parameter validation engine that verifies request inputs before sending them to the server. This ensures incorrect values are detected immediately and reported with clear, actionable error messages.

Instead of discovering mistakes after an API request fails, developers receive instant feedback directly in their scripts.

This validation layer is one of the key reliability features of the SDK.

Note: Parameter validation behaves identically across all three client classes (SanerClient, PlatformClient, CvemClient); the examples below use flat resource access (client.Organization..., client.Device...) as if client is a PlatformClient/CvemClient scoped to that resource's product, since that's the shortest form. On a SanerClient instance, prefix with the namespace instead: client.platform.Organization..., client.cvem.Device.... See the "Client Types" guide for the full comparison.


Why Parameter Validation Matters

Client-side validation provides several advantages:

  • prevents invalid API calls from reaching the server
  • reduces debugging time
  • improves automation reliability
  • detects integration mistakes early
  • produces clear error messages explaining what went wrong
  • eliminates unnecessary network requests

Example:

response = client.Organization.get(organization=123)

Error raised:

TypeError: organization must be string; got 123 (int)

The request never reaches the API server, saving time and avoiding runtime failures.


What an Error Message Tells You

Every validation error names three things where it can: the parameter, what was expected, and what was actually supplied. The trailing got ... clause is there so a correction never has to be re-derived, which matters most for the pattern rules, where the expectation alone is hard to act on:

ValueError: groupname has invalid format (expected ^[A-Za-z0-9._\- ]+$); got 'dev team!'
TypeError: port must be string; got 8080 (int)

For a list parameter the clause is reported against the failing element, so emails[1] must be string; got 123 (int) points at the one entry to fix.

Three deliberate exceptions:

  • Secret parameters are never echoed. Any parameter whose name marks it a credential (password, proxypassword, and anything matching the same stems) reports only the rule it broke, never the value. A validation error is what ends up in your own logging stack, so password must contain at least one digit is all you get.
  • Email addresses are not echoed either, since the value is personal data and the expected format is already stated.
  • Length rules report the length, not the value: username must be <= 50 characters (got 63).

Echoed values are truncated at 2048 characters, so a large payload cannot bloat the message.


Discover Parameter Types Directly in Your IDE

The Saner SDK provides rich inline docstrings that allow developers to view parameter requirements directly inside their IDE.

When hovering over a method or its parameters, you can instantly see:

  • parameter type requirements
  • required vs optional parameters
  • allowed enum values
  • formatting expectations
  • usage notes

Example:

client.Organization.get(organization="ExampleOrg")

Hovering over:

organization

will display its expected type and description inside your editor.

This enables developers to identify correct parameter formats without switching to external documentation.

For more details, see the "IDE Docstring Support" guide.


Mandatory Field Validation

Required parameters are enforced automatically.

If a required parameter is missing or empty, the SDK raises an exception immediately.

Example:

response = client.Organization.remove(name="")

Error:

ValueError: name cannot be empty

This prevents incomplete payloads from being sent to the API server.


At Least One Of (Grouped Requirements)

Some endpoints have no single mandatory parameter, but do require at least one of a group. Device.scan is the clearest example: it accepts a hostname, an IP address, or a MAC address, and needs at least one of them to know which device to scan.

The SDK enforces the group as a whole, so calling such a method with none of its alternatives fails before a request is sent.

Example:

response = client.Device.scan()

Error:

ValueError: ScanDeviceModel requires at least one of: hostname, ip, mac. The server answers a request carrying none of them with HTTP 200 and a FAIL body, so it is rejected here rather than sent.

Supplying any one of them satisfies the rule:

response = client.Device.scan(hostname="workstation-01")

This check is worth more than it first appears. The Saner API answers a request carrying no device identifier with HTTP 200 and a FAIL status inside the body, which reads like an empty result set rather than a rejection. Validating the group locally turns that into an immediate, explicit error instead.

Two details worth knowing:

  • An empty string does not satisfy the rule. client.Device.scan(hostname="") behaves the same as passing nothing at all, because an empty optional value is dropped before the payload is built.
  • A malformed value reports its own error first. client.Device.scan(ip="not-an-ip") raises ValueError: ip must be valid IPv4 address; got 'not-an-ip', not the group error, so you always see the more specific problem.

Methods documented to return everything when called without a filter, such as Account.get and Organization.get, are unaffected and still accept an empty call.


Type Validation

Each parameter is validated against its expected type before execution.

Supported validations include:

  • string values
  • integers
  • list[str]
  • list[dict]
  • dict[str, str]
  • structured JSON payloads

Example:

response = client.Organization.get(organization=123)

Error:

TypeError: organization must be string; got 123 (int)

This ensures the request matches the expected API schema exactly.


Enum Validation (Allowed Values Only)

Parameters restricted to predefined values are validated automatically.

Example validator definition:

role = Field(required=True, enum=["ADMIN", "ORGADMIN", "ACCOUNTADMIN", "NORMAL"])

Valid usage:

role="ORGADMIN"

Invalid usage:

role="SUPERADMIN"

Error:

ValueError: role must be one of ['ADMIN', 'ORGADMIN', 'ACCOUNTADMIN', 'NORMAL']; got 'SUPERADMIN'

This prevents unsupported configuration values from reaching the API server. Every enum parameter lists its accepted values in the method docstring, so check there rather than guessing.


Format Validation

Structured formats are validated automatically.

Supported formats include:

FormatExample
email[email protected]
IPv4192.168.1.10
MAC addressAA-BB-CC-DD-EE-FF
date2026-04-01
numeric string"12345"
float string"12.34"
boolean string"true", "false"
numeric list"1", "1,2", "8,9,10"
time of day03:30:PM, 18:30:00
date and time2023-09-25.18:00:00, 2023-09-25 18:00:00

Example:

email="admin.example.com"

Error:

ValueError: email must be a valid email

Every format rule matches the entire value, not just a leading portion, so a value that looks valid at the start but carries something extra is rejected rather than silently truncated to its valid-looking prefix:

Both raise ValueError: email must be a valid email. Control characters and embedded whitespace are rejected outright, since a value like the first example is designed to smuggle extra content into a field the server later treats as an email header.

IPv4 validation is similarly strict about ambiguous input:

ip="010.0.0.1"
ValueError: ip must be valid IPv4 address; got '010.0.0.1'

A leading zero is rejected rather than silently interpreted as decimal: some resolvers read 010 as octal (8), so accepting it would let the same string mean two different addresses depending on what parses it downstream.

This ensures parameters follow correct formatting before transmission.


Date and Time Formats

Scheduling parameters (starttime, startwindowtime, endtime, autoreboottime, rebootdatetime, schedule, schedulevalue) are validated against the format the API documents for them. Before this, a wrong-format time was the one mistake that had to reach the server to be caught:

client.Patch.createRemediationJob(..., starttime="9am")
ValueError: starttime has invalid format (expected ^(([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]|([01]?[0-9]|2[0-3]):[0-5][0-9]:(?i:AM|PM))$); got '9am'

Each method's docstring states the format that method's parameter expects, so check there first. The shapes in use are:

ShapeExampleUsed by
HH:MM:AM/PM03:30:PMstarttime, startwindowtime, endtime on most methods
HH:MM:SS18:30:00starttime and endtime on Compliance.createMisconfigurationRollbackTask
YYYY-MM-DD.HH:MM:SS2023-09-25.18:00:00autoreboottime (note the dot)
YYYY-MM-DD HH:MM:SS2023-09-25 18:00:00rebootdatetime on the Endpoint deployment and uninstall methods (note the space)
YYYY-MM-DD2024-12-29schedule, schedulevalue
YYYY-MM-DD or immediateimmediateschedule on the Patch job-creation methods

Two notes on how strict this is:

  • Both time shapes are accepted wherever a time of day is expected. The API documentation is not consistent about which of the two a given parameter takes, so both pass validation and the server decides. Follow the docstring for the method you are calling; the pattern only rejects shapes that are documented nowhere, such as "9am", "09:00 AM" (a space rather than the API's colon) or a bare "21:00".
  • rebootdatetime accepts either separator, a space or a dot, because the API reference documents 2019-06-29 00:00:00 for Endpoint.addSoftwareDeployment and 2019-06-29.00:00:00 for Endpoint.uninstallSoftware. autoreboottime is documented consistently with a dot and only accepts that.

Reboot Flags and Schedule Components

forcereboot and autoreboot accept "TRUE" or "FALSE" in either case, since the API reference documents both spellings, sometimes within a single method:

forcereboot="TRUE"    # accepted
forcereboot="false"   # also accepted
forcereboot="yes"     # rejected

The schedule component parameters (monthoftheyear, weekofthemonth, dayoftheweek, dayofthemonth) each accept one or more comma-separated numbers, not a single number:

weekofthemonth="1"      # accepted
dayoftheweek="3,6"      # accepted
monthoftheyear="8,9,10" # accepted
dayoftheweek="mon"      # rejected

Length Validation

Parameters with length constraints are validated automatically.

Example validator definition:

username = Field(min_length=5, max_length=50)

Invalid usage:

username="abc"

Error:

ValueError: username must be at least 5 characters (got 3)

Another example:

username="a" * 100

Error:

ValueError: username must be <= 50 characters (got 63)

Boolean String Validation

Some API parameters accept boolean values as strings, spelled in lowercase.

Example:

hostpeerverification="yes"

Error:

ValueError: hostpeerverification must be either true or false; got 'yes'

Correct usage:

hostpeerverification="true"

This rule is strict about case, which is right for the parameters the API documents only in lowercase (hostpeerverification, usecredential, secureldap, bufferpatchesenabled). It is deliberately not used for forcereboot and autoreboot, which the API documents in both cases: see the "Reboot Flags and Schedule Components" section above. Note also that a yes/no parameter such as sslverify is an enum of "yes"/"no", not a boolean string.


Password Strength Validation

Credential parameters enforce strong password rules automatically.

Requirements include:

  • minimum 8 characters
  • maximum 100 characters
  • at least one digit
  • at least one uppercase character
  • at least one lowercase character
  • at least one special character

Example:

password="weakpass"

Error:

ValueError: password must contain a special character

This helps enforce secure integrations by default.


List Validation

List-based parameters are validated element-by-element.

Example:

emails=["[email protected]", 123]

Error:

ValueError: emails[1] must be string; got 123 (int)

Supported list validation types:

  • list[str]
  • list[email]
  • list[dict]

Combining List Validation with Format and Length Rules

A list[str] parameter can also carry the same format and length rules used for a single string: each rule is then applied to every element of the list, and a failure is reported with its index.

Example validator definition:

groupname = Field(
    required=True,
    list_string=True,
    max_length=50,
    regex=r"^[A-Za-z0-9._\- ]+$",
)

Valid usage:

groupname=["prod-servers", "db.cluster"]

Invalid usage, with one entry exceeding the length limit:

groupname=["prod-servers", "x" * 100]

Error:

ValueError: groupname[1] must be <= 50 characters (got 100)

Invalid usage, with one entry failing the pattern:

groupname=["../../etc/passwd"]

Error:

ValueError: groupname[0] has invalid format (expected ^[A-Za-z0-9._\- ]+$); got '../../etc/passwd'

This applies to any scalar rule (max_length, min_length, regex, email, enum, date, and similar) whenever it is combined with list_string=True.


IPv4 and MAC Address Validation

Network-related parameters are validated for correctness.

Example:

ip="192.168.1.999"

Error:

ValueError: ip must be valid IPv4 address; got '192.168.1.999'

Example:

mac="invalid-mac"

Error:

ValueError: mac must be valid MAC address; got 'invalid-mac'

JSON Payload Validation

Structured JSON inputs must be valid dictionaries or lists.

Example:

payload="invalid"

Error:

TypeError: payload must be valid JSON object (dict or list); got 'invalid'

Example Validation Flow

When calling any SDK method, validation occurs in the following order:

Step 1 → required field validation
Step 2 → type validation
Step 3 → format validation
Step 4 → enum validation
Step 5 → length validation
Step 6 → request execution

If any validation step fails, execution stops immediately with a clear error message.


Benefits for Developers

The validation system improves integration quality by:

  • catching mistakes instantly
  • preventing malformed API requests
  • reducing debugging cycles
  • providing human-readable error messages
  • enforcing correct parameter usage
  • improving production automation stability

These safeguards make the Saner Python SDK safer and easier to use across both small scripts and enterprise-scale automation workflows.


Did this page help you?