{"record":{"id":"79a8dfd9e25cf315","repo":"openzipkin/zipkin","slug":"canceled","errorCode":null,"errorMessage":"Canceled","messagePattern":"Canceled","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"zipkin/src/main/java/zipkin2/Call.java","lineNumber":381,"sourceCode":"      return new ErrorHandling<>(errorHandler, delegate.clone());\n    }\n  }\n\n  public static abstract class Base<V> extends Call<V> {\n    volatile boolean canceled;\n    boolean executed;\n\n    protected Base() {\n    }\n\n    @Override public final V execute() throws IOException {\n      synchronized (this) {\n        if (this.executed) throw new IllegalStateException(\"Already Executed\");\n        this.executed = true;\n      }\n\n      if (isCanceled()) {\n        throw new IOException(\"Canceled\");\n      } else {\n        return this.doExecute();\n      }\n    }\n\n    protected abstract V doExecute() throws IOException;\n\n    @Override public final void enqueue(Callback<V> callback) {\n      synchronized (this) {\n        if (this.executed) throw new IllegalStateException(\"Already Executed\");\n        this.executed = true;\n      }\n\n      if (isCanceled()) {\n        callback.onError(new IOException(\"Canceled\"));\n      } else {\n        this.doEnqueue(callback);\n      }","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/openzipkin/zipkin/blob/878ce2a1fad54ca941d17fdcf2e1d924b148eb1f/zipkin/src/main/java/zipkin2/Call.java#L363-L399","documentation":"Call.Base.execute throws IOException('Canceled') when you execute a Call after cancel() was called on it. Cancellation in Zipkin is cooperative: it sets a flag rather than interrupting work, so a later execute() checks isCanceled() and fails with an IOException (cancellation is treated as an I/O condition, matching the method's throws clause).","triggerScenarios":"Calling call.cancel() (e.g. from a timeout handler or another thread) and then call.execute(); also a race where cancel() wins between the executed-flag check and doExecute().","commonSituations":"Timeout wrappers that cancel a call but let the original execute() proceed; cleanup code that cancels all in-flight calls during shutdown while a worker still tries to execute one.","solutions":["Treat IOException with message 'Canceled' as a normal outcome: do not retry it, just stop the work that owned the Call.","Avoid calling execute() on calls you may cancel; coordinate with a flag or Future so only one path acts on the Call.","For timeouts, prefer cancelling the whole operation (and discarding the Call) instead of cancelling then executing."],"exampleFix":"// before\nnew Thread(() -> call.cancel()).start();\nList<Span> spans = call.execute(); // may throw IOException(\"Canceled\")\n\n// after\nif (!call.isCanceled()) {\n  try {\n    List<Span> spans = call.execute();\n  } catch (IOException e) {\n    if (\"Canceled\".equals(e.getMessage())) return; // cancelled elsewhere: stop quietly\n    throw e;\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (!call.isCanceled()) { result = call.execute(); }","typeGuard":null,"tryCatchPattern":"try { return call.execute(); } catch (IOException e) { if (\"Canceled\".equals(e.getMessage())) return null; // cancelled elsewhere: not an error\n throw e; }","preventionTips":["Only one component should own a Call: either the canceller or the executer","Do not retry 'Canceled' IOExceptions; they are intentional stop signals","Prefer cancelling the enclosing operation, not the raw Call, in timeout wrappers"],"tags":["core","call","cancellation","io-exception","concurrency"],"backgroundTag":null,"analyzedSha":"878ce2a1fad54ca941d17fdcf2e1d924b148eb1f","analyzedAt":"2026-08-14T15:17:09.895Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}