Vega SDK — Python

Troubleshooting

Diagnose Capability package, Provider lifecycle, execution, testing, and utility failures. This page covers the public SDK only.

Start with the environment

Record these facts before changing code:

bash
python --version
python -m pip --version
python -m pip show vega-capability-sdk
python -c "import vega_capability_sdk; print(vega_capability_sdk.__version__)"
vega-capability --help

Run commands from the Capability project root and activate the same virtual environment used to install the Capability.

Installation and import problems

vega-capability: command not found

  • The SDK is not installed in the active environment.
  • The virtual environment is not active.
  • The environment's script directory is not on PATH.

Verify:

bash
python -m pip show vega-capability-sdk
python -c "import sys; print(sys.executable)"

Reinstall into the active environment:

bash
python -m pip install "vega-capability-sdk>=0.1.0,<0.2.0"

ModuleNotFoundError for the Provider entrypoint

Package validation does not import the entrypoint. A manifest can validate even when the Python module is not installed. Check the manifest value:

yaml
provider:
entrypoint: example_echo.provider:EchoProvider

Then verify the same module and class directly:

bash
python -c "from example_echo.provider import EchoProvider; print(EchoProvider)"

Use an installable package layout and install it:

bash
python -m pip install -e ".[test]"

Do not fix this by adding machine-specific source directories to production PYTHONPATH.

SDK version range is incompatible

capability.yaml declares the accepted SDK range:

yaml
vega:
sdkVersion: ">=0.1.0,<0.2.0"

Compare it with:

bash
python -c "import vega_capability_sdk; print(vega_capability_sdk.__version__)"

Install an accepted SDK version or update the declared range only after compatibility testing. Do not bypass the check.

Manifest and schema failures

Missing capability.yaml

Run validation against the directory that directly contains capability.yaml:

bash
vega-capability validate /path/to/capability

Unsupported apiVersion or kind

The current SDK accepts:

yaml
apiVersion: vcp.shyda.ai/v1alpha1
kind: Capability

Do not invent a newer value before the SDK supports it.

Invalid Capability or Action ID

Capability IDs use lowercase letters, digits, ., _, and -, beginning with a lowercase letter or digit. Action IDs contain at least two lowercase dot-separated segments:

yaml
metadata:
id: example.echo
actions:
- id: example.echo.run

Schema file not found or path escapes the package root

Schema and configuration paths are resolved relative to the Capability root. They must identify files inside that root.

Valid:

yaml
inputSchema: schemas/input.json

Rejected:

yaml
inputSchema: ../shared/input.json

Copy or package the required schema inside the Capability instead of traversing outside it.

Invalid JSON Schema

Schema files must contain a JSON object and valid JSON Schema Draft 2020-12. Validate the package and inspect the reported schema context:

bash
vega-capability validate . --json

progress=true but no Progress Schema

Declare both the execution behavior and schema:

yaml
progressSchema: schemas/progress.json
execution:
progress: true

Progress payloads must match the declared schema when a validator is injected.

Unknown resource reference

Every Action resource must be declared at package level:

yaml
resources:
- id: speaker
mode: exclusive
actions:
- id: example.speech.speak
resources: [speaker]

The SDK validates the declaration; Runtime performs resource arbitration.

Provider lifecycle failures

ProviderLifecycleError: Cannot execute Provider in new

Public execution requires successful initialization:

python
await provider.initialize(initialization_context)
result = await provider.execute(request, execution_context)

Tests should normally use ProviderContractHarness or the fake context factories instead of manually skipping lifecycle steps.

Initialization failed and init_context is unavailable

When on_initialize() raises, base state becomes failed and the context is not retained. Fix the initialization error before reading init_context. Initialization can be attempted again from failed.

create_background_task() fails during initialization

The base Provider is initializing while on_initialize() runs, but managed task creation requires ready. Create the task after await super().initialize(context) in a carefully overridden public initialize, or lazily from a ready execution path. Preserve the base lifecycle call.

Unexpected exception became ProviderInternalError

BaseCapabilityProvider.execute() preserves CapabilityError subclasses and wraps other ordinary exceptions. Map expected backend conditions explicitly:

python
try:
result = await self._backend.call()
except ConnectionError as exc:
raise DependencyUnavailableError(
"Backend is unavailable",
cause=exc,
) from exc

Do not expose raw vendor exception text unless it has been reviewed for secrets.

Execution data failures

Request and context execution IDs do not match

Build both objects with the same execution_id. The base Provider validates this before calling on_execute.

TypeError while modifying request or configuration

Request inputs, metadata, configuration, result data, error details, and several manifest mappings are deeply frozen. Construct a new dictionary:

python
output = {**request.inputs, "accepted": True}

Do not mutate the original mapping.

Value is not JSON-compatible

SDK-facing JSON data supports:

  • None.
  • Booleans.
  • Numbers.
  • Strings.
  • Mappings with string keys.
  • Lists and tuples containing supported values.

Convert bytes, datetimes, enums, paths, dataclasses, and vendor objects to an intentional JSON representation or Artifact reference.

Naive datetime rejected

Deadlines and health timestamps must be timezone-aware:

python
from datetime import UTC, datetime
deadline = datetime.now(UTC)

Artifact service is unavailable

context.artifacts is optional:

python
if context.artifacts is None:
raise DependencyUnavailableError("Artifact storage is unavailable")

Do not silently fall back to returning a local absolute path.

Cancellation and physical operations

Provider does not stop promptly

Check cancellation:

  • Before starting backend work.
  • Between bounded work units.
  • Before and after external I/O.
  • While waiting for a long-running Provider-controlled operation.

If a vendor call blocks the event loop or cannot be interrupted, adapt it behind a controlled backend interface and document the limitation.

Cancellation raised, but hardware may still be moving

Cancellation of SDK or Python work is not physical-stop confirmation. Request a backend stop and report only evidence the backend supplies. Use unknown when you cannot establish the physical state.

The primary cancellation reason did not change

CancellationToken uses first-reason-wins semantics. Later distinct reasons are available as secondary reasons but do not replace the primary reason.

Test failures

pytest is not installed

Install the Capability's test extra:

bash
python -m pip install -e ".[test]"

Async test is not executed correctly

Install pytest-asyncio and configure:

toml
[tool.pytest.ini_options]
asyncio_mode = "auto"

Conformance report fails output_schema

Pass the parsed ActionDefinition to the harness and make Provider output match its schema:

python
report = await ProviderContractHarness().run(
provider,
request,
action=action,
)

The harness validates output only when action is supplied.

Progress events are unavailable in a test

Pass and retain an explicit collector:

python
collector = ProgressCollector()
context = fake_execution_context(collector=collector)

When omitted, the factory creates an internal collector that is not returned.

Recovery failures

Contradictory probe result rejected

The SDK rejects these combinations:

  • succeeded with error.
  • failed with result.
  • not_found, unknown, or unavailable with either result or error.

Report only the observation supported by the backend.

Cannot distinguish missing operation from unavailable service

Return unavailable when the query service cannot be reached. Return not_found only when the service successfully establishes that the operation does not exist. Otherwise return unknown. Do not replay work inside a recovery probe.

Secret exposure

SecretValue is redacted when passed as a wrapper to StructuredLogger. After get_secret_value(), the result is a normal string and cannot be recognized automatically.

If a secret appears in logs, output, diagnostics, Progress, an error, or a committed file:

  • Revoke or rotate it according to your security procedure.
  • Remove it from all public data paths.
  • Review retained logs and artifacts.
  • Add a regression test for the leak.

Information to include in a support report

Include:

  • SDK and Python versions.
  • Operating system and architecture.
  • The command that failed.
  • A minimal redacted manifest or code example.
  • Exception type and sanitized message.
  • Whether the failure reproduces with SDK test contexts.
Do not include credentials, tokens, private keys, personal data, proprietary Runtime internals, or unreviewed vendor responses.