Com.couchbase.lite.CouchbaseLiteException: read failed: EBADF (Bad file number) + Document.update - java.lang.NullPointerException

Hello everyone,

I am facing an issue, which produces a crash of the app.

Scenario :

-MainActivity.OnCreate : My app create a doc with a push/pull continous replication.
Doc is created in CBLite and in CouchServers too.
-MainActivity.OnCreate : My app add and attachment (picture) to the newly created doc.
-MainActivity.BroadcastReceiver : My app receive an intent with new data, and i then call the code to update the document with some of these data.

-CRASH ! nullPointerException on "document.update(new Document.DocumentUpdater()"

I check the value of “document” just before in my logcat and is is not null.

If i restart the application (2nd launch)… no crash…everything is ok.
If i remove the step to add the attachment (picture)…everything is ok even at first launch

Is it possible that the BroadcastReceiver uses a different thread that the one used by the DocumentUpdater ?
I dont understand the error…could anyone explain me the cause and how to solve it ?

Best regards @ Couchbase’s community !

Function

private void updateProfileLocation(Database database, String documentId) {

   String data = readFromFile(fileNameUserProfileData); 
   JSONObject jsonObj = null;             
   JSONObject subObj = null;

try {
    jsonObj = new JSONObject(data);              
    subObj = jsonObj.getJSONObject("userinfo"); 
} catch (JSONException e1) {
    e1.printStackTrace();
}
        try {
            final String mLastUpdateTime= subObj.getString("mLastUpdateTime");
            Document document = database.getDocument(documentId);

            document.update(new Document.DocumentUpdater() {
                // Update the document with more data
                @Override
                public boolean update(UnsavedRevision newRevision) {
                    Map<String, Object> properties = newRevision.getUserProperties();
                    properties.put("lastUpdateTime", mLastUpdateTime);      //
                    newRevision.setUserProperties(properties);
                    return true;
                }
            });
        } catch (CouchbaseLiteException | JSONException e) {
            e.printStackTrace();
        }

**Logs **

 07-07 01:15:29.064  12114-12114/com.xxxxxxx.xxxxxxW/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41744c68)
    07-07 01:15:29.064  12114-12114/com.xxxxxx.xxxxxxE/AndroidRuntime﹕ FATAL EXCEPTION: main
        Process: com.xxxxxxx.xxxxxx, PID: 12114
        java.lang.NullPointerException
                at com.couchbase.lite.Database.generateIDForRevision(Database.java:3223)
                at com.couchbase.lite.Database.putRevision(Database.java:3622)
                at com.couchbase.lite.Database.putRevision(Database.java:3465)
                at com.couchbase.lite.Document.putProperties(Document.java:415)
                at com.couchbase.lite.UnsavedRevision.save(UnsavedRevision.java:102)
                at com.couchbase.lite.Document.update(Document.java:275)
                at com.xxxx.xxxx.MainActivity.updateProfileLocation(MainActivity.java:803)
                at com.xxxxx.xxxxx.MainActivity.access$1000(MainActivity.java:158)
                at com.xxxxx.xxxxxx.MainActivity$4.onReceive(MainActivity.java:446)
                at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
                at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
                at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
                at android.os.Handler.dispatchMessage(Handler.java:102)
                at android.os.Looper.loop(Looper.java:136)
                at android.app.ActivityThread.main(ActivityThread.java:5081)
                at java.lang.reflect.Method.invokeNative(Native Method)
                at java.lang.reflect.Method.invoke(Method.java:515)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
                at dalvik.system.NativeStart.main(Native Method)
    07-07 01:17:09.251  12114-12271/com.xxxxx.xxxxxW/Sync﹕ com.couchbase.lite.replicator.PullerInternal@42123540 no new remote revisions to fetch.  add lastInboxSequence (5) to pendingSequences (com.couchbase.lite.support.SequenceMap@42173630)

What version of the library are you running?

com.couchbase.lite:couchbase-lite-android:1.0.4

Hi @heretyk

Could you please share code snippet of setting the attachment?

Thanks!

Hello @hideki,

I just saw your response.
I will provide you the code as soon as possible.

Hello hideki,

Here is my code for uploading the attachment :

private class UploadPictureTask extends AsyncTask<File, String, Double> {

@Override
protected Double doInBackground(File...params) {
Document document = database.getDocument(userId);
if(document != null)
{ /* BEGIN  : Save Picture to Database */
    try {
        ContextWrapper cw = new ContextWrapper(getApplicationContext());
        File pictureLocation = cw.getDir("imageDir", Context.MODE_PRIVATE); // path to /data/data/yourapp/app_data/imageDir
        File picture1=new File(pictureLocation, MainActivity.fileNamePicture1 + ".jpg");
        File picture2=new File(pictureLocation, MainActivity.fileNamePicture2 + ".jpg");
        File picture3=new File(pictureLocation, MainActivity.fileNamePicture3 + ".jpg");
        InputStream stream = new FileInputStream(picture1);
        // Add or update an image to a document as a JPEG attachment:
        Document doc = database.getDocument(userId);
        UnsavedRevision newRev = doc.getCurrentRevision().createRevision();
        newRev.setAttachment(fileNamePicture1 + ".jpg", "image/jpeg", stream);
        if (picture2.exists())
        {
            InputStream stream2 = new FileInputStream(picture2);
            newRev.setAttachment(fileNamePicture2 + ".jpg", "image/jpeg", stream2);
        }
        if (picture3.exists())
        {
            InputStream stream3 = new FileInputStream(picture3);
            newRev.setAttachment(fileNamePicture3 + ".jpg", "image/jpeg", stream3);
        }
        newRev.save();
    }  catch (FileNotFoundException | CouchbaseLiteException e) {
        e.printStackTrace();
    }
}/* CLOSE  : Save Picture to Database */
    return null;
}

Another question as we are on this :

newRev.setAttachment(fileNamePicture2 + “.jpg”, “image/jpeg”, stream2);

Will it upload this attachment even if the remote version is the same ?
Or there is a native mechanism that check if files are different or not ?

If you explicitly add an attachment to a revision, it will get uploaded by the replicator even if the server already has an identical attachment.

Hi @heretyk,

It seems codes of both writing attachments to a db and updating documents are correct. It seems there could be bug in our codes. If you can provide simple sample application to reproduce this issue, it is really helpful.

Ticket Number

Thanks,
Hideki

Unfortunately, i won’t be able to provide you a sample application.

But this scenario is easily reproducible

I just re-tried it :

  1. i take a picture with the phone (in my app, an intent trigger the camera)
  2. I save file locally on the phone
  3. I call the method UploadPictureTask that i provided you earlier
  4. Then i got the crash with the following exception:

07-28 15:44:05.914 1467-1467/com.xxxxxxxx.xxxxxxE/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.xxxxx.xxxxx, PID: 1467
java.lang.RuntimeException: Unable to read from stream.
at com.couchbase.lite.BlobStoreWriter.read(BlobStoreWriter.java:94)
at com.couchbase.lite.Attachment.blobStoreWriterForBody(Attachment.java:214)
at com.couchbase.lite.Attachment.installAttachmentBodies(Attachment.java:193)
at com.couchbase.lite.Document.putProperties(Document.java:402)
at com.couchbase.lite.UnsavedRevision.save(UnsavedRevision.java:102)
at com.couchbase.lite.Document.update(Document.java:275)
at com.xxxxx.xxxxx.MainActivity.updateProfileLocationinDB(MainActivity.java:778)
at com.xxxxx.xxxxx.MainActivity.access$900(MainActivity.java:159)
at com.xxxxx.xxxxx.MainActivity$4.onReceive(MainActivity.java:450)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5081)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.io.IOException: read failed: EBADF (Bad file number)
at libcore.io.IoBridge.read(IoBridge.java:435)
at java.io.FileInputStream.read(FileInputStream.java:179)
at java.io.InputStream.read(InputStream.java:162)
at com.couchbase.lite.BlobStoreWriter.read(BlobStoreWriter.java:87)
at com.couchbase.lite.Attachment.blobStoreWriterForBody(Attachment.java:214)
at com.couchbase.lite.Attachment.installAttachmentBodies(Attachment.java:193)
at com.couchbase.lite.Document.putProperties(Document.java:402)
at com.couchbase.lite.UnsavedRevision.save(UnsavedRevision.java:102)
at com.couchbase.lite.Document.update(Document.java:275)
at com.xxxxx.xxxxx.MainActivity.updateProfileLocationinDB(MainActivity.java:778)
at com.xxxxx.xxxxx.MainActivity.access$900(MainActivity.java:159)
at com.xxxxx.xxxxx.MainActivity$4.onReceive(MainActivity.java:450)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5081)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)
Caused by: libcore.io.ErrnoException: read failed: EBADF (Bad file number)
at libcore.io.Posix.readBytes(Native Method)
at libcore.io.Posix.read(Posix.java:128)
at libcore.io.BlockGuardOs.read(BlockGuardOs.java:149)
at libcore.io.IoBridge.read(IoBridge.java:425)
at java.io.FileInputStream.read(FileInputStream.java:179)
at java.io.InputStream.read(InputStream.java:162)
at com.couchbase.lite.BlobStoreWriter.read(BlobStoreWriter.java:87)
at com.couchbase.lite.Attachment.blobStoreWriterForBody(Attachment.java:214)
at com.couchbase.lite.Attachment.installAttachmentBodies(Attachment.java:193)
at com.couchbase.lite.Document.putProperties(Document.java:402)
at com.couchbase.lite.UnsavedRevision.save(UnsavedRevision.java:102)
at com.couchbase.lite.Document.update(Document.java:275)
at com.xxxxx.xxxxx.MainActivity.updateProfileLocationinDB(MainActivity.java:778)
at com.xxxxx.xxxxx.MainActivity.access$900(MainActivity.java:159)
at com.xxxxx.xxxxx.MainActivity$4.onReceive(MainActivity.java:450)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5081)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)
07-28 15:44:22.150 1467-1696/com.xxxxx.xxxxx W/Sync﹕ com.couchbase.lite.replicator.PullerInternal@428a0f28 no new remote revisions to fetch. add lastInboxSequence (3087) to pendingSequences (com.couchbase.lite.support.SequenceMap@429e05f0)



5.After that, when i relaunch the app, the “UpdateProfileLocation” executes successfully 3-4 times then the app crashes again with the following exception :

07-28 15:54:14.818 1541-1541/com.xxxxx.xxxxx D/AndroidRuntime﹕ Shutting down VM
07-28 15:54:14.818 1541-1541/com.xxxxx.xxxxx W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41becc68)
07-28 15:54:14.848 1541-1541/com.xxxxx.xxxxx E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.xxxxx.xxxxx, PID: 1541
java.lang.NullPointerException
at com.couchbase.lite.Database.generateIDForRevision(Database.java:3223)
at com.couchbase.lite.Database.putRevision(Database.java:3622)
at com.couchbase.lite.Database.putRevision(Database.java:3465)
at com.couchbase.lite.Document.putProperties(Document.java:415)
at com.couchbase.lite.UnsavedRevision.save(UnsavedRevision.java:102)
at com.couchbase.lite.Document.update(Document.java:275)
at com.xxxxx.xxxxx.MainActivity.updateProfileLocationinDB(MainActivity.java:778)
at com.xxxxx.xxxxx.MainActivity.access$900(MainActivity.java:159)
at com.xxxxx.xxxxx.MainActivity$4.onReceive(MainActivity.java:450)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:297)
at android.support.v4.content.LocalBroadcastManager.access$000(LocalBroadcastManager.java:46)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:116)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5081)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)
07-28 15:55:16.848 1541-1762/com.xxxxx.xxxxx W/Sync﹕ com.couchbase.lite.replicator.PullerInternal@42312fb0 no new remote revisions to fetch. add lastInboxSequence (3113) to pendingSequences (com.couchbase.lite.support.SequenceMap@42306be0)

6. Another important point is that attachment are uploaded to CouchServer. So, anyhow, it worked once and attachment were uploaded…

I will try to retry this on a clean device/install, and check if, depite of exceptions, attachment is uploaded or not.

Major update :

The exception in “5.” above, which occured when restarting app, doesn’t occurs anymore after some minutes.
The app start correcty, and update to document works fine, as is the replication.

I believe a corrupted revision might be the cause…Maybe that the fact that uploading the attachment produces a crash, leads to corrupted revision( or indexes of revisions) which leads to the second exception at restart.

It is just an initial thought…maybe it could help you to understand this issue ?

Regards,

Major update 2 :

Well no, it restarted crashing a few minutes after, same exception :

“java.lang.NullPointerException
at com.couchbase.lite.Database.generateIDForRevision”

produced by this call :

md5Digest.update(attachment.getBlobKey().getBytes()

@hideki : Do you have any feedback on this matter ?

Best regards,

Hi @heretyk,
Issue ticket is still in the queue. I will try to look into it as soon as possible.
Thanks,
Hideki

Hi @heretyk,

From the following stack trace you provided, it seems Couchbase Lite library failed to read data from InputStream when updating document. Following NullPointerException could be caused by this.

There are two possibilities.

  1. Attachments were not stored correctly when they were stored in the database.
  2. Some reason, loading attachment information failed during document update.

Can you do following tests?

  1. Just after image files were stored in database, these attachments can be accessible? Purpose of this: Make sure if attachments were stored correctly
  2. Check if updating document which is triggered by Button presses could cause same problem. Purpose of this: Identify if BroadcastReceiver could cause this error.

Thanks!
Hideki

HI @heretyk,

Are you using CouchDB or Couchbase Server with Sync Gateway for server side?
Which version of servers are you using?

Also, Is it possible to try to use Couchbase Lite for Android v1.1.0? v1.1.0 has many bug fixes.

Thanks!

Hi,

I am using Couchbase server 3.0.3-1716 Enterprise Edition (build-1716)
My application is using CBL 1.0.4 on Android and my sync gateway is 1.0.4-34

I’ve openned a ticket because i can’t updte to 1.1.0, android studio (Gradle) can’t built it if i compile with CBL 1.1.0 :

Execution failed for task ‘:app:dexDebug’.

com.android.ide.common.process.ProcessException:
org.gradle.process.internal.ExecException: Process ‘command
‘C:\Travail\Java\jdk1.7.0_79\bin\java.exe’’ finished with non-zero exit
value 2

Concerning your tests, i just made a new test on a new doc, on a new installation (re installed a package on my android device).
I got the same exception.
But i noticed that the picture was correctly uploaded. I see the attachment either localy and also on my main servers in the Document :

{
“_attachments”: {
“picture1.jpg”: {
“content_type”: “image/jpeg”,
“digest”: “sha1-odfw5TvRYqZH+kfC3IewaodhmDE=”,
“length”: 524660,
“revpos”: 4,
“stub”: true
}
},


But while analyzing my application log, i noticed something :

In fact, i use 2 different AsyncTask :
-The first for getting a cookie from the gateway, then “onPostExecute” method, i start replication
-The second for uploading the picture

I am wondering if the fact that replication is not set up whereas i am already uploading the attachment might causes this issue ?

But if that’s the case, why my document and the attachment is perfectly built both in my couchbase Server and in my device’s too ?

Because before the crash ( on Document.update), the replication and all the queries works fine. (during a few seconds).

As an example, in my previous test, my app successfully made 4 revisions on the doc…the crash occured in the 5th rev.

Hope it might help you…

Last thing, just after the upload of attachment, i see this in my log :

08-06 18:33:42.078 20646-28932/com.xxxxxx.xxxxxxW/com.couchbase.lite﹕ Sync: Error converting lastSequence: null to long. Using 0
08-06 18:33:42.088 20646-28930/com.xxxxx.xxxxxW/com.couchbase.lite﹕ Sync: com.couchbase.lite.replicator.PullerInternal@42643840: starting ChangeTracker with since=null mode=OneShot
08-06 18:33:42.088 20646-28930/com.xxxx.xxxxW/com.couchbase.lite﹕ Sync: com.couchbase.lite.replicator.PullerInternal@42643840: started ChangeTracker com.couchbase.lite.replicator.ChangeTracker@42b4ef78
08-06 18:33:42.339 20646-28923/com.xxxx.xxxxW/com.couchbase.lite﹕ Sync: com.couchbase.lite.replicator.PullerInternal@42643840: Received invalid doc ID from _changes: {seq=3825, id=_user/BlueberryHill2015_7_6_1438878751447, changes=[]}

08-06 18:33:42.349 20646-28938/com.xxxx.xxxxE/com.couchbase.lite﹕ Sync: PullerInternal stopGraceful.run() finished

**Update : **

I restarted the app. So 1h30 since the last test.

Well the replication, and updates works perfectly. The document datas are perfectly correct.
And the attachment is still there.

Almost 50 revisions done so far…no error…