Dropbox.read error (connection lost) on reading taskpaper file from Dropbox

I have an iOS shortcut that lets me add new Taskpaper actions, which were then added to Drafts and subsequently written to my todo.taskpaper file in Dropbox. I was using an adapted version of this action: Add Task(s) to a Taskpaper File Inbox in Dropbox | Drafts Directory

Connection timeout error.
More recently I’ve been encountering hiccups, where the first time during the day I use the script it works, but if I add new items right after (or a few hours later) I get an error that it can’t read the dropbox file (and therefore it can’t overwrite). I’ve added some error logging printed to a Draft and the Dropbox.lastError I get is the following: “The network connection was lost.” :

Error Log (2024-10-03T14:28:26.577Z):

Failed to read file: /Apps/Taskpaper/todo.taskpaper
Dropbox error: The network connection was lost.

It’s tough to debug as sometimes it works, however when re-running the Action again right after I get persistently this error. I’ve already tried to re-authorise the Dropbox connection without result.

Could there be recent changes in the Dropbox API workings, caching or on iOS side? This is my go-to daily shortcut to capture todo’s so it’s a bit inconvenient I can’t get it to work reliably anymore. :smiling_face_with_tear:

Full script (with the debugging part now):

const filePathAndName = "/Apps/Taskpaper/todo.taskpaper";
const inboxHeaderName = "Inbox:";

const tasks = getAndCleanTasksToAdd();
const textToBeWorkedUpon = openAndCheckTaskpaperFile(filePathAndName);
const newFileContent = constructNewFileConent(textToBeWorkedUpon, tasks);

overwriteTaskpaperFileToDropbox(filePathAndName, newFileContent);

function getAndCleanTasksToAdd() {
  const taskText = editor.getText();
  return taskText.split(/\r\n|\r|\n/).filter((line) => line.trim().length > 0);
}

function openAndCheckTaskpaperFile(filePathAndName) {
  const draft = Draft.create();
  const dropbox = Dropbox.create();
  
  try {
    const fileContent = dropbox.read(filePathAndName);

    if (fileContent === undefined || fileContent === null) {
      let errorMsg = `Failed to read file: ${filePathAndName}\nDropbox error: ${dropbox.lastError}\n${dropbox.lastErrorCode}\n${dropbox.lastErrorNessage}`;
      console.log(errorMsg); // This will show in the script console
      logError(errorMsg); // This will create a new draft with the error message
      throw new Error(`Unable to read file ${filePathAndName}`);
    }

    draft.content = fileContent;

    const trimmedContent = fileContent.trim();
    if (!trimmedContent.startsWith(inboxHeaderName)) {
      throw new Error(
        `File ${filePathAndName} doesn't seem to be in the expected Taskpaper format or has an incorrect inbox header`
      );
    }

    console.log("File open, checked and ready for task(s) import");
    return draft.content;
  } catch (error) {
    let errorMsg = `Error reading file: ${error.message}`;
    console.log(errorMsg); // This will show in the script console
    logError(errorMsg); // This will create a new draft with the error message
    throw error;
  }
}

function logError(message) {
  let errorDraft = Draft.create();
  errorDraft.content = `Error Log (${new Date().toISOString()}):\n\n${message}`;
  errorDraft.update();
}


function constructNewFileConent(fileContent, tasks) {
  const inboxPrefix = `${inboxHeaderName}\n\t`;
  const newTasksText = tasks.reverse().map((task) => `- ${task}`).join("\n\t");

  return fileContent.replace(inboxPrefix, `${inboxPrefix}${newTasksText}\n\t`);
}

function overwriteTaskpaperFileToDropbox(filePathAndName, newFileContent) {
  const dropbox = Dropbox.create();
  const success = dropbox.write(filePathAndName, newFileContent, "overwrite", true);

  if (!success || dropbox.read(filePathAndName) !== newFileContent) {
    console.error(dropbox.lastError);
    context.fail();
  } else {
    console.log("File contents match!");
  }
}

// © Rapid Definition 2020
// Software is provided as is, with no support or warranty implied

I don’t know of any general issues with Dropbox connectivity. That’s not an error Drafts is generating, but something it’s getting back from the underlying HTTP call, so not sure what to make of it.

How big is that file you are writing? Is it especially large, or has it gotten significantly larger than it used to be, such that it might be triggering some problems?

These calls do go directly to Dropbox over the network, so are susceptible to potential slowdowns or connectivity issues. Since this seems to be coming from Dropbox’s end, it’s possible it might be advantages to consider switching to using FileManager scripting and a Bookmarked folder in your Dropbox to target the file instead of the APIs. On the plus side this would be writing locally and not require network connectivity – on the downside, it might not as immediately sync to Dropbox as it would by relying on Dropbox’s apps to do the sync in the background after the file was updated.

Thanks, it’s indeed something I’ve been looking into but the docs mention that is most cased the API is more suitable indeed. :slight_smile:

It’s a small text file (86kb and 1686 lines). :thinking:

@agiletortoise i just stumbled on this post which looks like the same issue (first request works but after that it fails): GET request to /files/download fails with "The net... - Page 2 - The Dropbox Community

Could it be cache related?

It could be. The thread seems to suggest a possible workaround is to request a different file, then request the right file again…but the file must exist.

Could you try putting a dummy small text file in your Dropbox, and add an extra call to fetch that file, discarding the result, then fetch the one you really want? See if that improves reliability.

Thanks for your support! Ok, I adjusted the script (thanks Claude Sonnet 3.5…) with a call to dummy.txt and actually, it resolved the issue! Tried 10 times in a row and it surprisingly never failed.

This version works as a workaround for now (although it feels a tiny bit slower):

const filePathAndName = "/Apps/Taskpaper/todo.taskpaper";
const dummyFilePath = "/Apps/Taskpaper/dummy.txt";
const inboxHeaderName = "Inbox:";

const tasks = getAndCleanTasksToAdd();
const textToBeWorkedUpon = openAndCheckTaskpaperFile(filePathAndName);
const newFileContent = constructNewFileConent(textToBeWorkedUpon, tasks);

overwriteTaskpaperFileToDropbox(filePathAndName, newFileContent);

function getAndCleanTasksToAdd() {
  const taskText = editor.getText();
  return taskText.split(/\r\n|\r|\n/).filter((line) => line.trim().length > 0);
}

function readDummyFile(filePath) {
  const dropbox = Dropbox.create();
  try {
    console.log(`Reading dummy file: ${filePath}`);
    const dummyContent = dropbox.read(filePath);
    console.log("Dummy file read successfully. Discarding content.");
    // We're intentionally not doing anything with the dummy content
  } catch (error) {
    console.log(`Error reading dummy file: ${error.message}`);
    // We're not throwing an error here, as we want to continue with the main file read
  }
}

function openAndCheckTaskpaperFile(filePathAndName) {
  const draft = Draft.create();
  const dropbox = Dropbox.create();
  
  // First, read the dummy file to potentially clear any cache
  readDummyFile(dummyFilePath);
  
  try {
    console.log(`Now reading the actual file: ${filePathAndName}`);
    const fileContent = dropbox.read(filePathAndName);

    if (fileContent === undefined || fileContent === null) {
      let errorMsg = `Failed to read file: ${filePathAndName}\nDropbox error: ${dropbox.lastError}\nError Code: ${dropbox.lastErrorCode}\nError Message: ${dropbox.lastErrorMessage}`;
      console.log(errorMsg);
      logError(errorMsg);
      throw new Error(`Unable to read file ${filePathAndName}`);
    }

    draft.content = fileContent;

    const trimmedContent = fileContent.trim();
    if (!trimmedContent.startsWith(inboxHeaderName)) {
      throw new Error(
        `File ${filePathAndName} doesn't seem to be in the expected Taskpaper format or has an incorrect inbox header`
      );
    }

    console.log("File open, checked and ready for task(s) import");
    return draft.content;
  } catch (error) {
    let errorMsg = `Error reading file: ${error.message}`;
    console.log(errorMsg);
    logError(errorMsg);
    throw error;
  }
}

function logError(message) {
  let errorDraft = Draft.create();
  errorDraft.content = `Error Log (${new Date().toISOString()}):\n\n${message}`;
  errorDraft.update();
}

function constructNewFileConent(fileContent, tasks) {
  const inboxPrefix = `${inboxHeaderName}\n\t`;
  const newTasksText = tasks.reverse().map((task) => `- ${task}`).join("\n\t");

  return fileContent.replace(inboxPrefix, `${inboxPrefix}${newTasksText}\n\t`);
}

function overwriteTaskpaperFileToDropbox(filePathAndName, newFileContent) {
  const dropbox = Dropbox.create();
  const success = dropbox.write(filePathAndName, newFileContent, "overwrite", true);

  if (!success || dropbox.read(filePathAndName) !== newFileContent) {
    console.log(`Error writing file: ${dropbox.lastError}`);
    console.log(`Error Code: ${dropbox.lastErrorCode}`);
    console.log(`Error Message: ${dropbox.lastErrorMessage}`);
    context.fail();
  } else {
    console.log("File contents match!");
  }
}

// © Rapid Definition 2020
// Software is provided as is, with no support or warranty implied
1 Like

I’ll look into it some more. Seems like I might be able to address by adding some caching headers to the API calls, as well.

3 Likes

This is now fixed since version 45.1. :raised_hands: Thanks!!

1 Like