Cookwala / حزمة التطوير والسيناريوهات / 049
A malformed request gets a Problem, a well-formed one gets a status
Two different answers: a request that is not a valid ExecuteRequest is answered with application/problem+json and never reaches the device; a valid request the device will not cook gets an ExecutionStatus in state refused.
اقرأ أولًا: /docs/CORE/ /docs/HUB/
الخطوات
Send a request without recipeHash. The answer is a Problem with title invalid-request naming the missing field.
startExecutionSend a request that claims core 0.1.0. The Problem title is unsupported-version and it carries the refusal code.
startExecutionA well-formed request is accepted and returns an ExecutionStatus.
startExecution
النتيجة
You can tell a transport or schema error from a device refusal in your client. Next: scenario 050 covers a duplicate execution id.
سطر الأوامر
cookwala hub --recipes examples --speed 200
curl -si -X POST http://localhost:7878/v1/executions -H 'Content-Type: application/json' -H 'Idempotency-Key: bad-request-01' -d '{"core":"0.2.0","kind":"ExecuteRequest","id":"ex-bad"}'المخرجات المسجلة
الحالة: ok
[1] startExecution: {"type": "https://cookwala.ai/errors/invalid-request", "title": "invalid-request", "detail": "missing recipeHash"}
[2] startExecution: {"type": "https://cookwala.ai/errors/unsupported-version", "title": "unsupported-version", "refusal": "unsupported_version"}
[3] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-049-ok", "seq": 0, "state": "accepted", "request": "ex-049-ok", "updatedAt": "2026-10-07T00:54:31Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by": "human"…الكود
التشغيل: python scenarios/out/049-malformed-requests-are-problems-not-refusals/python.py
# Scenario 049: A malformed request gets a Problem, a well-formed one gets a status
# Two different answers: a request that is not a valid ExecuteRequest is answered with application/problem+json and never reaches the device; a valid request the device will not cook gets an ExecutionStatus in state refused.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem
def load(path):
with open(path, encoding="utf-8") as f:
return json.load(f)
def pick(obj, path):
for part in path.replace("]", "").replace("[", ".").split("."):
obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
return obj
c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))
# Step 1: Send a request without recipeHash. The answer is a Problem with title invalid-request naming the missing field.
try:
missing = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-049-missing", "recipe": "cw:cookwala.ai:example-shakshuka", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-049-missing-a"}, "ex-049-missing-key", True)
except CookwalaProblem as p:
missing = p.body # a refusal is a result, not a crash
print("refused:", p.refusal)
assert pick(missing, "title") == "invalid-request", pick(missing, "title")
print("ok", "title", "=", "invalid-request")
assert pick(missing, "detail") == "missing recipeHash", pick(missing, "detail")
print("ok", "detail", "=", "missing recipeHash")
# Step 2: Send a request that claims core 0.1.0. The Problem title is unsupported-version and it carries the refusal code.
try:
version = c.start_execution({"core": "0.1.0", "kind": "ExecuteRequest", "id": "ex-049-version", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-049-version-a"}, "ex-049-version-key", True)
except CookwalaProblem as p:
version = p.body # a refusal is a result, not a crash
print("refused:", p.refusal)
assert pick(version, "title") == "unsupported-version", pick(version, "title")
print("ok", "title", "=", "unsupported-version")
assert pick(version, "refusal") == "unsupported_version", pick(version, "refusal")
print("ok", "refusal", "=", "unsupported_version")
# Step 3: A well-formed request is accepted and returns an ExecutionStatus.
try:
ok = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-049-ok", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-049-ok-a"}, "ex-049-ok-key", True)
except CookwalaProblem as p:
ok = p.body # a refusal is a result, not a crash
print("refused:", p.refusal)
assert pick(ok, "kind") == "ExecutionStatus", pick(ok, "kind")
print("ok", "kind", "=", "ExecutionStatus")
assert pick(ok, "state") == "accepted", pick(ok, "state")
print("ok", "state", "=", "accepted")
print("scenario complete")
scenarios/049-malformed-requests-are-problems-not-refusals.json