{"record":{"id":"23bf5cfb8120447c","repo":"iflytek/astron-agent","slug":"s3-upload-error-23bf5c","errorCode":"S3_UPLOAD_ERROR","errorMessage":"S3_UPLOAD_ERROR","messagePattern":"S3_UPLOAD_ERROR","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"error","filePath":"console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/common/ImageService.java","lineNumber":66,"sourceCode":"\n        final long size = file.getSize();\n\n        final String original = file.getOriginalFilename();\n        final String safeName = buildSafeFileName(original, contentType);\n        final String objectKey = \"icon/user/\" + safeName;\n\n        try (InputStream in = file.getInputStream()) {\n            if (size > 0) {\n                // Known content length: prefer direct upload\n                s3UtilClient.putObject(objectKey, in, size, contentType);\n            } else {\n                // Unknown content length: fallback to multipart upload\n                s3UtilClient.putObject(objectKey, in, contentType, MULTIPART_PART_SIZE);\n            }\n        } catch (Exception e) {\n            log.error(\"Upload image failed, name={}, size={}, type={}, err={}\",\n                    original, size, contentType, e.getMessage(), e);\n            throw new BusinessException(ResponseEnum.S3_UPLOAD_ERROR);\n        }\n        return objectKey;\n    }\n\n    /**\n     * Check whether the given Content-Type is allowed.\n     * <p>\n     * Fallback allows any {@code image/*} if needed.\n     * </p>\n     *\n     * @param contentType HTTP Content-Type of the file\n     * @return {@code true} if allowed; {@code false} otherwise\n     */\n    private static boolean isAllowedType(String contentType) {\n        if (contentType == null)\n            return false;\n        for (String t : ALLOWED_TYPES) {\n            if (t.equalsIgnoreCase(contentType))","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/common/ImageService.java#L48-L84","documentation":"ImageService.upload wraps all exceptions raised while storing the object (S3/MinIO putObject, stream retrieval, multipart upload) into a BusinessException with code S3_UPLOAD_ERROR. The original error (name, size, type, message) is logged but replaced in the thrown exception, so any storage-layer failure surfaces as this single code.","triggerScenarios":"putObject/multipart upload throws: S3/MinIO unreachable, bucket missing, credentials rejected, bucket policy denies PutObject, network interruption mid-stream, or input stream already consumed (file.getInputStream() reused).","commonSituations":"MinIO endpoint/credentials misconfigured in the environment; bucket not created or wrongly named; object key policy producing invalid keys; MinIO down or out of disk; IAM/STS policy lacking s3:PutObject.","solutions":["Check the error log 'Upload image failed' for the underlying S3 error (bucket, credentials, connectivity).","Verify S3/MinIO endpoint, access key, secret key, and bucket name in configuration, and that the bucket exists.","Ensure the credentials/instance policy allow s3:PutObject on the target bucket.","Confirm network reachability from the toolkit service to the S3/MinIO endpoint.","Include the cause in the thrown exception to avoid masking the root error."],"exampleFix":"// before\nthrow new BusinessException(ResponseEnum.S3_UPLOAD_ERROR);\n\n// after\nthrow new BusinessException(ResponseEnum.S3_UPLOAD_ERROR, e);","handlingStrategy":"retry","validationCode":"// Pre-flight: bucket reachable and credentials valid\ns3UtilClient.headBucket(bucket); // throws early if missing/no permission","typeGuard":null,"tryCatchPattern":"try {\n    String key = imageService.upload(file);\n} catch (BusinessException e) {\n    if (\"S3_UPLOAD_ERROR\".equals(e.getMessage()) || ResponseEnum.S3_UPLOAD_ERROR.getCode().equals(e.getCode())) {\n        // transient storage failure: bounded retry, then surface an upload-unavailable error\n        return retryWithBackoff(2);\n    }\n    throw e;\n}","preventionTips":["Verify MinIO/S3 endpoint, credentials, and bucket existence in every environment's deploy checklist.","Grant s3:PutObject on the target bucket to the service identity.","Monitor object-store connectivity and disk usage; alert before failures occur.","Each upload should read a fresh InputStream to avoid consumed-stream errors."],"tags":["s3","minio","storage","upload","java"],"backgroundTag":"http-request-failed","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}