{"record":{"id":"699966fedb6179a4","repo":"apache/hadoop","slug":"closed-has-error-bs-pre-write-obs-s-has-error","errorCode":null,"errorMessage":"closed has error. bs : pre write obs[%s] has error.","messagePattern":"closed has error\\. bs : pre write obs\\[(.+?)\\] has error\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSBlockOutputStream.java","lineNumber":417,"sourceCode":"   *\n   * <p>This will not return until the upload is complete or the attempt to\n   * perform the upload has failed. Exceptions raised in this method are\n   * indicative that the write has failed and data is at risk of being lost.\n   *\n   * @throws IOException on any failure.\n   */\n  @Override\n  public synchronized void close() throws IOException {\n    if (closed.getAndSet(true)) {\n      // already closed\n      LOG.debug(\"Ignoring close() as stream is already closed\");\n      return;\n    }\n    if (hasException.get()) {\n      String closeWarning = String.format(\n          \"closed has error. bs : pre write obs[%s] has error.\", key);\n      LOG.warn(closeWarning);\n      throw new IOException(closeWarning);\n    }\n    // do upload\n    completeCurrentBlock();\n\n    // clear\n    clearHFlushOrSync();\n\n    // All end of write operations, including deleting fake parent\n    // directories\n    writeOperationHelper.writeSuccessful(key);\n  }\n\n  /**\n   * If flush has take place, need to append file, else to put object.\n   *\n   * @throws IOException any problem in append or put object\n   */\n  private synchronized void putObjectIfNeedAppend() throws IOException {","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSBlockOutputStream.java#L399-L435","documentation":"OBSBlockOutputStream.close() flips the closed flag, then checks hasException: if any prior operation on the stream failed, close() throws IOException('closed has error. bs : pre write obs[<key>] has error.') instead of performing the final multipart complete. The object is therefore NOT finalized in OBS — data may be partially uploaded — and the caller must treat the file as failed. As with write(), the informative exception happened earlier; this one only signals 'do not trust this file'.","triggerScenarios":"Any earlier write/flush/block-upload failure followed by a normal try-with-resources close(); background upload thread failing while the main thread finishes writing and closes; the completeCurrentBlock() or a prior part upload aborting asynchronously before close is reached.","commonSituations":"Jobs whose real error was logged minutes before the misleading 'close failed' symptom at task commit; monitoring that only surfaces the close() exception and misses the first stack; users assuming close() will 'flush through' problems and salvage partial data.","solutions":["Treat this as a failed write: locate the FIRST exception for the key in logs (network/auth/quota) and fix that.","Ensure the multipart upload is aborted to avoid orphaned parts billing: call ((OBSBlockOutputStream) out.getWrappedStream()).abort() in the catch, or rely on the filesystem's abort hooks; verify with the OBS console/lifecycle rule for incomplete MPU cleanup.","Retry the entire file write after the root cause is fixed — there is no way to resume a stream after this error.","Harden close paths: catch IOException from close() separately from the body so both original and close failures are reported."],"exampleFix":"// before\ntry (FSDataOutputStream out = fs.create(path)) {\n  writeAll(out, records); // earlier part-upload failure logged here\n} // close() now throws 'closed has error'\n\n// after\nFSDataOutputStream out = fs.create(path);\ntry {\n  writeAll(out, records);\n  out.close();\n} catch (IOException e) {\n  try { ((OBSBlockOutputStream) out.getWrappedStream()).abort(); } catch (IOException ignore) {}\n  throw new IOException(\"Write failed for \" + path + \" - see earlier root-cause exception\", e);\n}","handlingStrategy":"try-catch","validationCode":"// pre-check your own failure flag before close\nif (writeFailed) {\n  abortQuietly(out);\n} else {\n  out.close(); // may still throw if a background upload failed silently\n}","typeGuard":null,"tryCatchPattern":"try {\n  out.close();\n} catch (IOException e) {\n  if (String.valueOf(e.getMessage()).contains(\"pre write obs[\")) {\n    abortQuietly(out); // ensure multipart is aborted\n    LOG.error(\"object NOT finalized in OBS; treating write as failed\", e);\n    throw new WriteFailedException(path, e);\n  }\n  throw e;\n}\n// helper\nvoid abortQuietly(FSDataOutputStream o) {\n  try { if (o.getWrappedStream() instanceof OBSBlockOutputStream) ((OBSBlockOutputStream) o.getWrappedStream()).abort(); } catch (IOException ignore) {}\n}","preventionTips":["Catch close() failures separately from body failures; report both.","Confirm MPU aborts on failure paths; add an OBS lifecycle rule for incomplete multipart uploads as a backstop.","Treat any close() exception as 'file not written' — do not publish the path downstream."],"tags":["obs","huaweicloud","close","multipart-upload","data-integrity"],"backgroundTag":"broken-output-stream","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}