{"record":{"id":"2ca74360c2db2385","repo":"gofr-dev/gofr","slug":"w-s3-file-is-empty","errorCode":null,"errorMessage":"%w: S3 file is empty","messagePattern":"%w: S3 file is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/datasource/file/s3/file.go","lineNumber":153,"sourceCode":"\t// empty objects and backends that reject \"bytes=0-\" behave as before.\n\tif f.offset > 0 {\n\t\tinput.Range = aws.String(fmt.Sprintf(\"bytes=%d-\", f.offset))\n\t}\n\n\tres, err := f.conn.GetObject(context.TODO(), input)\n\tif err != nil {\n\t\tmsg = fmt.Sprintf(\"Failed to retrieve %q: %v\", fileName, err)\n\t\treturn 0, err\n\t}\n\n\t// Close the body from any previous read before replacing it, otherwise the\n\t// earlier HTTP connection leaks.\n\tf.closeBody()\n\n\tf.body = res.Body\n\tif f.body == nil {\n\t\tmsg = fmt.Sprintf(\"File %q is nil\", fileName)\n\t\treturn 0, fmt.Errorf(\"%w: S3 file is empty\", ErrNilResponse)\n\t}\n\n\t// If a byte range was requested (offset > 0) but the backend replied with the\n\t// whole object (no Content-Range), io.ReadFull would fill p from byte 0 and\n\t// silently serve the wrong bytes. Refuse rather than corrupt the read.\n\tif f.offset > 0 && res.ContentRange == nil {\n\t\tf.closeBody()\n\t\tf.body = nil\n\t\tmsg = fmt.Sprintf(\"Range request for %q not honored by backend\", fileName)\n\n\t\treturn 0, ErrRangeNotHonored\n\t}\n\n\t// normalizeReadFull fills p completely unless the object ends first, reporting\n\t// a short fill as io.EOF along with the real byte count — honoring the io.Reader\n\t// contract that n reflects the number of bytes actually read.\n\tn, err = normalizeReadFull(f.body, p)\n\tif err != nil && !errors.Is(err, io.EOF) {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/datasource/file/s3/file.go#L135-L171","documentation":"This error wraps ErrNilResponse: during (*S3File).Read, the GetObject call succeeded but the response body was nil, so the S3 file appears empty/unreadable and the read is aborted with 0 bytes. The wrapper message names the wrapped sentinel, so errors.Is(err, s3.ErrNilResponse) matches it.","triggerScenarios":"Any Read() call on an S3File where the backend returns a GetObjectOutput with a nil Body — malformed backend responses, incomplete mocks, or SDK/backend contract mismatches.","commonSituations":"S3-compatible stores (MinIO/SeaweedFS) returning degraded 200 responses; test stubs of GetObjectOutput without Body; intermittent proxy failures stripping the body.","solutions":["Confirm the object is fully retrievable with the AWS CLI and that the bucket/key are correct.","Fix or upgrade the S3-compatible backend/proxy so GetObject always streams a body on 200.","Populate Body in any mock/test client used with this datasource.","Check errors.Is(err, s3.ErrNilResponse) in your error handling and retry or fail the read explicitly."],"exampleFix":"// handling\nif err != nil {\n    if errors.Is(err, s3datasource.ErrNilResponse) {\n        // backend returned no body; retry or surface a 5xx\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"n, err := f.Read(buf)\nif err != nil && errors.Is(err, s3datasource.ErrNilResponse) {\n    // treat as failed read; retry or surface backend health issue\n    return fmt.Errorf(\"read %s: %w\", name, err)\n}","preventionTips":["Health-check the object store at startup.","Avoid stub clients that omit Body.","Monitor for degraded 200 responses from proxies.","Pin and update SDK versions together with gofr."],"tags":["s3","nil-response","read","gofr"],"backgroundTag":"nil-response-body","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}