{"record":{"id":"87d8a08c45e717f0","repo":"apache/druid","slug":"not-an-error-check-iserror-first","errorCode":null,"errorMessage":"Not an error; check isError first","messagePattern":"Not an error; check isError first","errorType":"exception","errorClass":"java.lang.IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/java/util/common/Either.java","lineNumber":76,"sourceCode":"    return error == null;\n  }\n\n  public boolean isError()\n  {\n    return error != null;\n  }\n\n  /**\n   * Returns the error object.\n   *\n   * @throws IllegalStateException if this instance is not an error\n   */\n  public L error()\n  {\n    if (isError()) {\n      return error;\n    } else {\n      throw new IllegalStateException(\"Not an error; check isError first\");\n    }\n  }\n\n  /**\n   * If this Either represents a value, returns it. If this Either represents an error, throw an error.\n   *\n   * If the error is a {@link DruidException}, it is thrown. If it is some other {@link Throwable}, it is\n   * wrapped in a {@link DruidException} and thrown. If it is not a throwable, a generic {@link DruidException}\n   * is thrown containing the string representation of the error object.\n   *\n   * To retrieve the error as-is, use {@link #isError()} and {@link #error()} instead.\n   */\n  @Nullable\n  public R valueOrThrow()\n  {\n    if (isValue()) {\n      return value;\n    } else if (error instanceof Throwable) {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/java/util/common/Either.java#L58-L94","documentation":"Either.error() returns the error (left) value only when this Either actually represents an error. Calling it on a value-holding Either breaks the contract, so an IllegalStateException is thrown telling the developer to check isError() first.","triggerScenarios":"Calling either.error() without first calling either.isError(), on an Either that holds a value.","commonSituations":"Misreading the Either contract after a map()/value() call; assuming error() returns null instead of throwing when a value is present; refactors that change which side is populated.","solutions":["Call isError() before accessing error()","Use valueOr / map / fold-style accessors that handle both sides","If you expected an error, inspect upstream logic that produced the Either"],"exampleFix":"// before\nErrorType err = either.error();\n// after\nif (either.isError()) {\n  ErrorType err = either.error();\n  handle(err);\n}","handlingStrategy":"type-guard","validationCode":"if (either.isError()) { /* safe to call either.error() */ }","typeGuard":"if (either.isError()) {\n  ErrorType err = either.error();\n} else {\n  ValueType val = either.value();\n}","tryCatchPattern":"try {\n  return either.error();\n} catch (IllegalStateException e) {\n  return defaultError; // Either held a value, not an error\n}","preventionTips":["Always gate error()/value() with isError()/isValue()","Prefer map()/fold()/valueOr combinators over raw accessors","Encode success/failure in types rather than nulls","Add assertions in tests covering both Either sides"],"tags":["illegal-state","api-misuse","result-type"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}