/api/task/cancel/{id}
POST SYNCRequest cancellation of a running or pending task.
Description
This endpoint requests cancellation of an asynchronous task. When called, it signals the task to stop execution as soon as possible. The cancellation is cooperative, meaning the task must check for cancellation requests and gracefully terminate its operations.
Task cancellation is not instantaneous. The task must periodically check for cancellation requests and may need time to clean up resources. After calling this endpoint, continue polling the task status to confirm when cancellation completes.
- User requested to stop a long-running operation
- Application needs to abort a task due to timeout or resource constraints
- Task parameters need to be changed, requiring restart with new values
- System shutdown or maintenance requiring all tasks to stop
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | The unique task identifier to cancel. This must be a valid task ID from an asynchronous operation. |
Response
Returns a 200 OK response if the cancellation request was accepted, 400 Bad Request if the task is already finished, or 404 Not Found if the task doesn't exist.
Success Response Properties
Example Response (Success - 200 OK)
{
"task_id": "a7f8c9d1-e234-56b7-8901-23c45de67f89",
"status": "cancelled",
"message": "Task cancellation requested successfully"
}Example Response (Task Already Finished - 400 Bad Request)
{
"error": "Cannot cancel task",
"message": "Task has already finished (state: COMPLETED)",
"task_id": "a7f8c9d1-e234-56b7-8901-23c45de67f89"
}Example Response (Task Not Found - 404 Not Found)
{
"error": "Task not found",
"message": "No task exists with ID: invalid-task-id",
"task_id": "invalid-task-id"
}Error Codes
| Status Code | Meaning | Description |
|---|---|---|
200 |
OK | Cancellation request accepted successfully |
400 |
Bad Request | Task is already finished (COMPLETED, FAILED, or CANCELLED) and cannot be cancelled |
404 |
Not Found | No task exists with the specified ID |
500 |
Internal Server Error | Server error occurred while processing the cancellation request |
Examples
JavaScript Example
const apiHost = 'http://localhost:8080';
// Cancel a task and verify cancellation
async function cancelTask(taskId) {
try {
// Request cancellation
const response = await fetch(`${apiHost}/api/task/cancel/${taskId}`, {
method: 'POST'
});
const result = await response.json();
if (response.ok) {
console.log('Cancellation requested:', result.message);
// Continue polling to confirm task reaches CANCELLED state
await waitForCancellation(taskId);
} else {
console.error('Cancellation failed:', result.message);
throw new Error(result.message);
}
} catch (error) {
console.error('Error cancelling task:', error);
throw error;
}
}
// Poll task status until it reaches CANCELLED state
async function waitForCancellation(taskId) {
const maxAttempts = 30; // 30 seconds
let attempts = 0;
while (attempts < maxAttempts) {
const statusResponse = await fetch(`${apiHost}/api/task/status/${taskId}`);
const task = await statusResponse.json();
console.log(`Task state: ${task.state}`);
if (task.state === 'CANCELLED') {
console.log('Task successfully cancelled');
return;
}
if (task.is_finished && task.state !== 'CANCELLED') {
console.log('Task finished before cancellation took effect');
return;
}
await new Promise(resolve => setTimeout(resolve, 1000));
attempts++;
}
console.warn('Timeout waiting for cancellation confirmation');
}
// Usage example
const taskId = 'a7f8c9d1-e234-56b7-8901-23c45de67f89';
cancelTask(taskId)
.then(() => console.log('Task cancelled successfully'))
.catch(error => console.error('Failed to cancel task:', error));
cURL Example
# Simple cancellation request
curl -X POST "http://localhost:8080/api/task/cancel/a7f8c9d1-e234-56b7-8901-23c45de67f89"
# Cancel task and check response status
TASK_ID="a7f8c9d1-e234-56b7-8901-23c45de67f89"
API_HOST="http://localhost:8080"
# Request cancellation
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$API_HOST/api/task/cancel/$TASK_ID")
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "HTTP Status: $HTTP_CODE"
echo "Response: $BODY" | jq '.'
# If successful, verify cancellation
if [ "$HTTP_CODE" = "200" ]; then
echo "Cancellation requested successfully"
# Poll until cancelled
while true; do
STATUS_RESPONSE=$(curl -s "$API_HOST/api/task/status/$TASK_ID")
STATE=$(echo "$STATUS_RESPONSE" | jq -r '.state')
echo "Current state: $STATE"
if [ "$STATE" = "CANCELLED" ]; then
echo "Task cancelled successfully"
break
fi
sleep 1
done
else
echo "Cancellation failed"
fi
Go Example
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
const apiHost = "http://localhost:8080"
type CancelResponse struct {
TaskID string `json:"task_id"`
Status string `json:"status"`
Message string `json:"message"`
}
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message"`
TaskID string `json:"task_id"`
}
type TaskStatus struct {
TaskID string `json:"task_id"`
State string `json:"state"`
IsFinished bool `json:"is_finished"`
}
func cancelTask(taskID string) error {
// Send cancellation request
url := fmt.Sprintf("%s/api/task/cancel/%s", apiHost, taskID)
resp, err := http.Post(url, "application/json", nil)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode == http.StatusOK {
var result CancelResponse
if err := json.Unmarshal(body, &result); err != nil {
return err
}
fmt.Printf("Cancellation requested: %s\n", result.Message)
// Wait for cancellation to complete
return waitForCancellation(taskID)
} else {
var errResp ErrorResponse
if err := json.Unmarshal(body, &errResp); err != nil {
return err
}
return fmt.Errorf("%s: %s", errResp.Error, errResp.Message)
}
}
func waitForCancellation(taskID string) error {
maxAttempts := 30 // 30 seconds
for i := 0; i < maxAttempts; i++ {
statusURL := fmt.Sprintf("%s/api/task/status/%s", apiHost, taskID)
resp, err := http.Get(statusURL)
if err != nil {
return err
}
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return err
}
var task TaskStatus
if err := json.Unmarshal(body, &task); err != nil {
return err
}
fmt.Printf("Task state: %s\n", task.State)
if task.State == "CANCELLED" {
fmt.Println("Task successfully cancelled")
return nil
}
if task.IsFinished && task.State != "CANCELLED" {
fmt.Println("Task finished before cancellation took effect")
return nil
}
time.Sleep(1 * time.Second)
}
return fmt.Errorf("timeout waiting for cancellation")
}
func main() {
taskID := "a7f8c9d1-e234-56b7-8901-23c45de67f89"
if err := cancelTask(taskID); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Task cancelled successfully")
}
Try It Out
Test task cancellation with a task ID from your QMail server:
This will actually attempt to cancel the task on your QMail server. Make sure you're using a valid task ID from a running or pending task. Already finished tasks will return a 400 error.
Related Endpoints
- /api/task/status/{id} - Check task status after cancellation