Start with the environment
Record these facts before changing code:
python --versionpython -m pip --versionpython -m pip show vega-capability-sdkpython -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:
python -m pip show vega-capability-sdkpython -c "import sys; print(sys.executable)"
Reinstall into the active environment:
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:
provider:entrypoint: example_echo.provider:EchoProvider
Then verify the same module and class directly:
python -c "from example_echo.provider import EchoProvider; print(EchoProvider)"
Use an installable package layout and install it:
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:
vega:sdkVersion: ">=0.1.0,<0.2.0"
Compare it with:
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:
vega-capability validate /path/to/capability
Unsupported apiVersion or kind
The current SDK accepts:
apiVersion: vcp.shyda.ai/v1alpha1kind: 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:
metadata:id: example.echoactions:- 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:
inputSchema: schemas/input.json
Rejected:
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:
vega-capability validate . --json
progress=true but no Progress Schema
Declare both the execution behavior and schema:
progressSchema: schemas/progress.jsonexecution: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:
resources:- id: speakermode: exclusiveactions:- id: example.speech.speakresources: [speaker]
The SDK validates the declaration; Runtime performs resource arbitration.
Provider lifecycle failures
ProviderLifecycleError: Cannot execute Provider in new
Public execution requires successful initialization:
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:
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:
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:
from datetime import UTC, datetimedeadline = datetime.now(UTC)
Artifact service is unavailable
context.artifacts is optional:
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:
python -m pip install -e ".[test]"
Async test is not executed correctly
Install pytest-asyncio and configure:
[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:
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:
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.
