Release Notes¶
0.31.0¶
Prelude¶
qiskit_serverless v0.31.0 introduces error event tracking for Qiskit Functions, structured JSON logging across all components, and a range of scheduler observability and reliability improvements.
New Features¶
Added comprehensive error tracking and event management capabilities for Qiskit Functions. This feature enables function developers to send custom error events via
send_error()or raise structured exceptions viaServerlessError, and allows users to retrieve and inspect these events for debugging and monitoring purposes.Function development
Function developers can now use two new capabilities:
A new
send_error()function to register custom error events that will be stored in the gateway and associated with the job. This is particularly useful for tracking application-specific errors, validation failures, or custom error conditions. Each event is represented by aJobEventobject.It can be used like this:
from qiskit_serverless import send_error # Send a simple error send_error( code="ERR001", message="Invalid input parameter", exception="ValueError" ) # Send an error with additional context send_error( code="ERR002", message="Circuit optimization failed", exception="OptimizationError", args={ "circuit_depth": 150, "num_qubits": 127, "optimization_level": 3 } )
A new
ServerlessErrorexception class to raise structured, trackable errors. When aServerlessErroris raised in a function, it is automatically caught by the framework, logged as an ERROR event, and can be retrieved by users through the events API.The
ServerlessErrorclass provides a structured way to communicate errors with:code: A unique error code for categorizationmessage: Human-readable error descriptiondetails: Additional context (can be any JSON-serializable data)
Example usage:
from qiskit_serverless import ServerlessError # Raise a structured error raise ServerlessError( code="VAL001", message="Invalid circuit depth", details={"max_depth": 100, "provided_depth": 150} )
Designing custom error classes
Function developers can subclass
ServerlessErrorto build domain-specific exception hierarchies. The framework records the name as theexceptionfield in the event, so subclass names appear directly in the events retrieved by users, making errors easier to categorize and filter.The simplest form requires no
__init__override:from qiskit_serverless import ServerlessError class ProviderError(ServerlessError): """Base error for all errors raised by this provider.""" class ValidationError(ProviderError): """Raised when input arguments fail validation.""" class ResourceError(ProviderError): """Raised when a required resource is unavailable."""
Raise them exactly as you would
ServerlessError:raise ValidationError( code="VAL001", message="num_qubits exceeds the maximum allowed value", details={"max": 127, "provided": 256} )
On the client side, the
exceptionfield of the retrieved event will be"ValidationError"rather than"ServerlessError", which lets users filter precisely:error_events = job.events(type="ERROR") for event in error_events: if event.data.get("exception") == "ValidationError": print("Input problem:", event.data["message"])
You can also override
__init__to fix acodeprefix or add mandatory fields, so callers only need to provide the varying parts:class CircuitError(ProviderError): """Raised for circuit-related failures.""" def __init__(self, message: str, circuit_id: str): super().__init__( code="CIR001", message=message, details={"circuit_id": circuit_id}, ) raise CircuitError("Circuit depth exceeds limit", circuit_id="circ-42")
When to use ServerlessError vs send_error
Use
ServerlessErrorwhen you want to stop execution and signal a fatal error that prevents the function from completing successfully. The error will be automatically logged, the job will transition to ERROR status, and the structured error message will be raised as aQiskitServerlessExceptionwhen the user callsjob.result():QiskitServerlessException: | Message: num_qubits exceeds the maximum allowed value | Code: VAL001 | Exception: ValidationError | Details: | - max: 127 | - provided: 256
Use
send_error()when you want to log an error but continue execution. This is useful for non-fatal errors, warnings, or when you want to track multiple issues without stopping the function. Events sent this way are still surfaced injob.result()if the job ultimately ends in ERROR status.
Example combining both approaches:
from qiskit_serverless import ServerlessError, send_error def process_circuits(circuits): failed = 0 for circuit in circuits: if circuit.num_qubits > 127: # Log non-fatal error, continue processing send_error( code="SKIP001", message="Circuit exceeds qubit limit", exception="ValidationError", args={"num_qubits": circuit.num_qubits} ) failed += 1 continue process(circuit) # Raise fatal error if all circuits failed if failed == len(circuits): raise ServerlessError( code="FATAL001", message="All circuits failed", details={"total": len(circuits)} )
Client side
Users can retrieve job events using the new
events()method available on both class:.Job objects and class:.ServerlessClient instances. Events can be filtered by type or retrieved in bulk for comprehensive debugging. Each event is represented by aJobEventobject with the following attributes:event_type: Type of event (e.g., “ERROR”, “STATUS_CHANGE”)origin: Where the event originated (e.g., “API”, “SCHEDULER”, “FUNCTION”)context: Context in which event was raised (e.g., “SEND_ERROR”, “SET_SUB_STATUS”)created: ISO 8601 timestamp of event creationdata: Dictionary containing event-specific data (for ERROR events: code, message, exception, args)
Retrieve all events for a job:
from qiskit_serverless import ServerlessClient serverless = ServerlessClient(...) job = serverless.run("my-function", arguments={"param": "value"}) # Get all events all_events = job.events() for event in all_events: print(f"Type: {event.event_type}") print(f"Created: {event.created}") print(f"Data: {event.data}")
To retrieve error events, users can filter events by type:
# Get only error events error_events = job.events(type="ERROR") for error in error_events: print(f"Error Code: {error.data.get('code')}") print(f"Message: {error.data.get('message')}") print(f"Exception: {error.data.get('exception')}") if 'args' in error.data: print(f"Additional Info: {error.data['args']}")
On top of the
Jobmethods, users can retrieve events using the client directly:# Alternative: use client to get events by job_id events = serverless.events(job_id="my-job-id", type="ERROR")
API
Two new REST API endpoints have been added:
GET /api/v1/jobs/{job_id}/events/: Retrieve job eventsPOST /api/v1/jobs/{job_id}/event/: Create a new job event
Upgrade Notes¶
The gateway, scheduler, and core components now emit structured JSON logs by default. Operators using log aggregation tools (e.g. Elasticsearch, Splunk) will see log lines formatted as JSON objects rather than plain text. The log level can still be configured via the existing
LOG_LEVELenvironment variable.
The scheduler now tracks metrics broken down per provider, enabling more granular observability of job throughput and resource usage when multiple providers are configured. No configuration change is required; existing dashboards will see new label dimensions on scheduler metrics.
Qiskit Serverless is now compatible with
qiskit-ibm-runtime==0.47. Users can upgrade their local installation before using this release:pip install "qiskit-ibm-runtime==0.47"
v0.30.1¶
Prelude¶
qiskit_serverless v0.30.1 is a small patch release that fixes a minor client issue, introduces a series of API/Gateway/Scheduler improvements that increase the health of the deployed service, and updates the base image with the newest dependency versions.
Bug Fixes¶
Fixed gateway triggering out of memory errors when downloading a big file. Now, the download uses a 64Kb stream buffer that prevents it from crashing.
Fixed a bug in
ServerlessClient.upload()where invalid files would not raise an error and instead fail to upload silently. The method now correctly raises aQiskitServerlessException.
v0.30.0¶
Prelude¶
The main feature in this release is the major refactor in logs to differentiate between provider logs and user logs and optimize storage. The logs are now stored in COS while keeping backward compatibility.
New Features¶
The
qiskit-serverlessPython library added two new utilities,get_logger()andget_provider_logger(), available to Qiskit Function developers. Developers should use these functions within their source code to distinguish which logging information is relevant for users (get_logger()) or providers (get_provider_logger()) respectively. These functions create[PUBLIC]and[PRIVATE]prefixes under the hood to allow the service to filter the information appropriately. To keep intellectual property safe, only function users can access user logs, and only providers can access provider logs. For example:from qiskit_serverless import get_logger, get_provider_logger user_logger = get_logger() provider_logger = get_provider_logger() get_logger().info("User log") user_logger.info("User multiline\nlog") user_logger.warning("User log") user_logger.error("User log") get_provider_logger().info("Provider log") provider_logger.info("Provider multiline\nlog") provider_logger.warning("Provider log") provider_logger.error("Provider log")
The
ServerlessClientandJobclasses’logs()method will from now on only return user logs. To access provider logs, we have added a newprovider_logs()method. For example:runnable_function = serverless_client.function("my_function") job = runnable_function.run() print(job.logs()) # only users print(job.provider_logs()) # only providers
Uploaded files now should have a valid file type in order to be accepted by the platform.
The accepted MIME types for uploaded files are:
application/x-tar (.tar)
application/gzip (.gz, .tgz)
application/json (.json)
application/octet-stream (generic binary; may include .bin, .dat, .exe)
application/zip (.zip)
text/plain (.txt, .log)
text/csv (.csv)
Upgrade Notes¶
Added a new
versionfield to theProgramDjango model. This field is used to track the version of the program (function).
Added a new
JobEventstable to our database to improve metrics and provide a more flexible foundation for future event-tracking features. Each time a job changes state, a new event is now recorded in the database.- The new
JobEventstable includes: id: auto-generated UUIDjob: foreign key referencing the jobs tablecreated: timestamp automatically set at event creationevent_type: string describing the event type (currently"Status change")context: string indicating where the change originated (e.g., Scheduler, API)data: JSON payload containing event‑specific details
- The new
Added several quality of life improvements in testing.
The Scheduler is modified to upload logs to COS when a job transitions from running to a terminal state. At that moment, the logs will be filtered based on the prefixes, and the information will be saved in the corresponding folder inside COS.
The Gateway endpoint
/logshas been modified to only target user logs, and a new /provider_logs` has been added for provider logs. Both should return: 1. The COS log for finished jobs, if it exists. This was previously filtered in the Scheduler. 2. The Ray console logs if the job is running. This is filtered live. 3. As a fallback, logs from the database.
Four new exception classes have been added to the gateway API to better differentiate between different failure scenarios:
JobNotFoundException,ProviderNotFoundException,FunctionNotFoundExceptionandFileNotFoundExceptionthat inherit from an abstractNotFoundErrorbase class.
Added a new gateway setting to limit the number of active jobs per user. By setting the environment variable
LIMITS_ACTIVE_JOBS_PER_USER, you can restrict the total number of jobs a user can have acrossQUEUED,PENDING, andRUNNINGstates. The default limit is50.
Requests to run programs will now return an
HTTP 429 Too Many Requestsresponse if a user attempts to start a new job while they are at their active job limit.
The Scheduler and API code have been refactored to improve their maintainability and scalability. Most changes are internal and do not affect the user experience, but some additions have been made, for example, the scheduler can now respond to
SIGINTandSIGTERMsignals, and the API now accepts a dynamic configuration system that allows changing application settings at runtime.
Deprecation Notes¶
The
@distribute_qiskit_functiondecorator has been deprecated and will be removed in following releases. The decorator was designed for remote program execution, a functionality that is now provided through the different serverless clients through theuploadmethod.
Bug Fixes¶
Fixed a bug in the function versioning workflow where the version set upon function creation couldn’t be uploaded to the platform, stored in the database, or retrieved upon function retrieval. After the fix, the function version can be tracked through the whole life cycle. For example:
from qiskit_serverless import QiskitFunction my-func = QiskitFunction( title="my_func_title", entrypoint="my_func_entrypoint.py", working_dir="./source_files/", version="2.0.0", ) # this print outputs 2.0.0 print(my-func.version) serverless.upload(my-func) my-func-load = serverless.function("my_func_title") # this print now also outputs 2.0.0 (previously empty) print(my-func-load.version)
Fixed an issue that affected local testing when using custom functions packaged as Docker images. These custom functions are registered as “provider” functions, however, their data path was resolved to a “custom” function data path, resulting in a “File not found” error when trying to fetch function arguments. After the fix, the data paths are resolved correctly, and no error is raised:
For user functions:
DATA_PATH = \data\usernameFor provider functions:
DATA_PATH = \data\username\providername\imagename
v0.29.0¶
Upgrade Notes¶
Refactored storage service initialization to accept
Programmodel instances instead of individual parameters. TheFileStorageandArgumentsStorageclasses now accept afunctionparameter (Program model instance) rather than separateusername,function_title, andprovider_nameparameters. This change improves code cohesion by reducing the number of parameters passed between components and ensures consistent access to program metadata across storage services.
Removed unused
function_titleandprovider_nameparameters fromProgramViewSet.run()method’s call tojob_serializer.save()as these parameters are no longer used after the storage service refactoring.
Removed
LocalClientandRayClientclasses and their usages in the tests. For testing, one should useServerlessClientwith default values as indocs/deployment/example_custom_image_function.rst. In addition, updated tests and removed irrelevant comments.
The legacy IBM Quantum authentication channel (
ibm_quantum) has been removed. Users should migrate to using the IBM Quantum Platform channel (ibm_quantum_platform) for authentication. The authentication logic has been simplified to support onlyibm_cloudandibm_quantum_platformchannels. When no channel header is provided and a CRN is present, the system defaults toibm_quantum_platform.
Qiskit Serverless now supports the latest version of the
qiskit-ibm-runtimepackage:qiskit-ibm-runtime==0.45.
Bug Fixes¶
Fixed a bug in the json decoder that would crop the response details to the first character when gateway errors were raised. This utility now forwards the full error message to the raised
QiskitServerlessException.
Fixed error handling when a function is not found. Instead of providing a bare 404 error, the
QiskitServerlessExceptionnow contains a more meaningful error message:QiskitServerlessException: | Message: Http bad request. | Code: 404 | Details: User program 'my-program' was not found or you do not have permission to view it.
Fixes https://github.com/Qiskit/qiskit-serverless/issues/1773.
Fixed a bug in
FunctionRepository.get_user_function()where functions with providers could be incorrectly returned when searching for user functions without providers. The method now correctly filters byprovider=Noneto ensure only user-owned functions without providers are returned. This prevents incorrect storage path resolution when users have multiple functions with the same title.
v0.28.0¶
New Features¶
IBMServerlessClientnow supports custom gateway host URLs through thehostparameter. This allows for a more streamlined testing of the client connecting to non-production gateways.
Added a new
ProgramHistorymodel and database table to track when entities perform actions to add or remove an instance from a program. These allow to audit access changes for bothinstancesandtrial_instances.
v0.27.1¶
Upgrade Notes¶
The scheduling logic that was managing non GPU and GPU jobs was improved. Now the scheduler manages both type of jobs independently so you can adjust separately where each of these jobs are run.
Qiskit Serverless now supports the latest version of the
qiskit-ibm-runtimepackage:qiskit-ibm-runtime==0.43.
Bug Fixes¶
Fixed a bug in the scheduler that was preventing it from stopping jobs automatically after the timeout threshold specified with the environment variable
PROGRAM_TIMEOUT.
v0.27.0¶
New Features¶
A new utility
get_runtime_service()is now available to simplify the instantiation of aQiskitRuntimeServicewithin a function. It automatically pulls credentials from environment variables, reducing boilerplate code and improving usability for function developers.Example usage:
from qiskit_serverless import get_runtime_service service = get_runtime_service() backend = service.backend("ibm_fez")
This is equivalent to:
import os from qiskit_serverless import get_arguments, save_result from qiskit_ibm_runtime import QiskitRuntimeService service = QiskitRuntimeService( channel=os.environ["QISKIT_IBM_CHANNEL"], instance=os.environ["QISKIT_IBM_INSTANCE"], token=os.environ["QISKIT_IBM_TOKEN"], ) backend = service.backend("ibm_fez")
This function also enables automatic tracking of runtime job and session IDs created during function execution. It’s important to note that tracking will only be possible if the function runtime jobs are submitted through a service instantiated with this function.
On top of the default credentials, function developers can customize the call to
get_runtime_service()with custom input parameters, includingtoken,instance,channelandurl. For example:from qiskit_serverless import get_runtime_service service = get_runtime_service(channel="ibm_quantum_platform", token="staging_token", instance="staging_crn", url="staging_url") backend = service.backend("staging_backend")
When using
get_runtime_service()inside a serverless function, the resulting job object now supports two new methods:job.runtime_jobs()andjob.runtime_sessions(). These methods return lists of job/session IDs that can be used to fetch job objects from aQiskitRuntimeServiceor access IQP dashboards.job.runtime_jobs()accepts an optionalruntime_sessionparameter that allows to filter the returned jobs by associated session id. For example:job = function.run(...) runtime_ids = job.runtime_jobs() # out = ["job_id_1", "job_id_2", "job_id_3"...] runtime_sessions = job.runtime_sessions() # out = ["session_id_1", "session_id_2"] # a specific session id can be passed to the runtime_jobs() method to filter by session: session_id = job.runtime_sessions()[0] # out = "session_id_1" session_job_ids = job.runtime_jobs(session_id) # in this example, only job ids 1 and 3 correspond to session_id_1: # out = ["job_id_1", "job_id_3"]
Upgrade Notes¶
Added a new AccessPolicy for User model to be able to allow or not the access to the service by the
is_activeparameter.
Enhanced
job.cancel()behavior to use the newly introduced features.job.cancel()now attempts to instantiate aQiskitRuntimeServiceusing the credentials from theServerlessClient, allowing automatic canceling of associated runtime jobs. This works when the credentials used in the function match those in the client, which happens by default when usingget_runtime_service()with no additional inputs.To support local testing or non-standard runtime URLs (e.g., staging environments), where the
ServerlessClientdon’t match those in theQiskitRuntimeServiceused to submit the jobs,job.cancel()accepts a service parameter:service = QiskitRuntimeService( channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN", url="my.staging.url.com" ) job.cancel(service)
The
ServerlessRuntimeServiceclass has been updated to support changes introduced inqiskit_ibm_runtime>=0.42, unlocking compatibility of the qiskit-serverless>=0.27.0 package withqiskit_ibm_runtime>=0.42. For older versions ofqiskit-serverless,qiskit_ibm_runtime<0.42will still be required.
The Scheduler will start to manage the size of the logs generated by the Qiskit Functions. The environment variable
FUNCTIONS_LOGS_SIZE_LIMITwill be in charge of the maximum size that the system will allow to store.
Enhanced
client.jobs()andfunction.jobs()behavior with new filters to improve the management of the different jobs. Now you can filter bystatusand bycreated_afterto retrieve those specific jobs. For example:# Filtering by status it will retrieve all the jobs with that status client.jobs(status="SUCCEEDED") # The same it will apply after a specific date time_filter = datetime.now(timezone.utc) client.jobs(created_after=time_filter) # And all these new filters can be combined with the Qiskit Function filter # to be able to return running jobs from the specific Qiskit Function function.jobs(status="RUNNING")