0

I am trying to read the contents of a Google Docs file by using the JavaScript googleapis API. The file exists and I can send the request, but I get a 404 error when reading the contents.

To reproduce:

  1. Create a Google Cloud project as described in Create Google Cloud project
  2. In the Google project, enable the Google Docs API
  3. Create a service account with Owner role
  4. Edit the service account to create a json service account key
  5. Run the following Node.js code to get a 404 error:
const { google } = require("googleapis")

const auth = new google.auth.GoogleAuth({
    keyFile: "<path_to_service_account_key_json_file>",
    scopes: [
        "https://www.googleapis.com/auth/documents"
    ]
})

async function getDocumentContents(documentId) {
    try {
        const authClient = await auth.getClient()
        const docs = google.docs({ version: "v1", auth: authClient })
        const response = await docs.documents.get({ documentId })
        return response.data
    } catch (error) {
        console.error(error)
    }
}

(async () => {
    const documentId = "2PACX-1vRMx5YQlZNa3ra8dYYxmv-QIQ3YJe8tbI3kqcuC7lQiZm-CSEznKfN_HYNSpoXcZIV3Y_O3YoUB1ecq"
    const documentContents = await getDocumentContents(documentId)
    console.info(documentContents.body.content)
})()

I would expect the file contents of this file to be printed to the console, but instead I get a 404 error. What am I missing?

This is the full error message:

GaxiosError: Requested entity was not found.
    at Gaxios._request (<path>/node_modules/gaxios/build/src/gaxios.js:142:23)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async JWT.requestAsync (<path>/node_modules/google-auth-library/build/src/auth/oauth2client.js:429:18)
    at async getDocumentContents (<path>/<file_name>.cjs:25:26)
    at async <path>/<file_name>.cjs:36:30 {
  config: {
    url: 'https://docs.googleapis.com/v1/documents/2PACX-1vRMx5YQlZNa3ra8dYYxmv-QIQ3YJe8tbI3kqcuC7lQiZm-CSEznKfN_HYNSpoXcZIV3Y_O3YoUB1ecq',
    method: 'GET',
    apiVersion: '',
    userAgentDirectives: [ [Object] ],
    paramsSerializer: [Function (anonymous)],
    headers: {
      'x-goog-api-client': 'gdcl/7.2.0 gl-node/22.11.0',
      'Accept-Encoding': 'gzip',
      'User-Agent': 'google-api-nodejs-client/7.2.0 (gzip)',
      Authorization: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.'
    },
    params: {},
    validateStatus: [Function (anonymous)],
    retry: true,
    responseType: 'unknown',
    errorRedactor: [Function: defaultErrorRedactor],
    retryConfig: {
      currentRetryAttempt: 0,
      retry: 3,
      httpMethodsToRetry: [Array],
      noResponseRetries: 2,
      retryDelayMultiplier: 2,
      timeOfFirstRequest: 1735753900182,
      totalTimeout: 9007199254740991,
      maxRetryDelay: 9007199254740991,
      statusCodesToRetry: [Array]
    }
  },
  response: {
    config: {
      url: 'https://docs.googleapis.com/v1/documents/2PACX-1vRMx5YQlZNa3ra8dYYxmv-QIQ3YJe8tbI3kqcuC7lQiZm-CSEznKfN_HYNSpoXcZIV3Y_O3YoUB1ecq',
      method: 'GET',
      apiVersion: '',
      userAgentDirectives: [Array],
      paramsSerializer: [Function (anonymous)],
      headers: [Object],
      params: {},
      validateStatus: [Function (anonymous)],
      retry: true,
      responseType: 'unknown',
      errorRedactor: [Function: defaultErrorRedactor]
    },
    data: { error: [Object] },
    headers: {
      'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000',
      'content-encoding': 'gzip',
      'content-type': 'application/json; charset=UTF-8',
      date: 'Wed, 01 Jan 2025 17:51:40 GMT',
      server: 'ESF',
      'transfer-encoding': 'chunked',
      vary: 'Origin, X-Origin, Referer',
      'x-content-type-options': 'nosniff',
      'x-frame-options': 'SAMEORIGIN',
      'x-l2-request-path': 'l2-managed-5',
      'x-xss-protection': '0'
    },
    status: 404,
    statusText: 'Not Found',
    request: {
      responseURL: 'https://docs.googleapis.com/v1/documents/2PACX-1vRMx5YQlZNa3ra8dYYxmv-QIQ3YJe8tbI3kqcuC7lQiZm-CSEznKfN_HYNSpoXcZIV3Y_O3YoUB1ecq'
    }
  },
  error: undefined,
  status: 404,
  code: 404,
  errors: [
    {
      message: 'Requested entity was not found.',
      domain: 'global',
      reason: 'notFound'
    }
  ],
  [Symbol(gaxios-gaxios-error)]: '6.7.1'
}
9
  • Can you include the full error message by editing the question above? Commented Jan 1 at 19:25
  • @JatsPPG full error message added Commented Jan 1 at 19:53
  • Can you try to change your documentId from the document ID of the file and not the ID from your published Docs? Commented Jan 2 at 17:03
  • 1
    @JatsPPG I tried the workaround again and now I can retrieve the contents of the new file with the Google Docs API. Thank you for your help Commented Jan 9 at 19:24
  • 1
    I'm glad to have helped. I recommend you post what you did as an answer so that other people from the community who have the same problem as yours know that theirs can be solved. Commented Jan 9 at 19:42

4 Answers 4

0

It seems the problem is that the Google Docs API doesn't allow access to published docs, only to docs that are shared with the appropriate permissions. So a workaround for accessing a published doc is to copy its contents to a shared doc and then access this doc instead.

Sign up to request clarification or add additional context in comments.

Comments

0

to access the file you need to share the file to the service account to do that you need to:

Find your service account email from your JSON credentials file - it looks like: [email protected]

Open your Google Doc in the browser

Click the "Share" button in the top right

Add the service account email as an editor/viewer

Make sure to check these settings:

"Notify people" should be unchecked Click "Send" or "Share" to confirm

hopes it work

1 Comment

Yes, all documents I tried are public but I still get a 404 for the published one
0

Ok so after banging my head against this question for a while I have found something interesting..

When using Document ID 2PACX we get a 404. At first I thought it was a something related to service account not having proper permissions. Just for the record:

  • My service account has owner rights
  • I made the doc public shareable i.e Anyone with link can open it
  • I even explicitly shared it with my service account

Still I got 404.

So, I tried a different method, I used the drive method to fetch all the documents and I found that the document that I wanted to fetch was associated to different ID.

Document ID from doc was "2PACX-1vTuwWllBnBa9StNEd1JzUI0sFi2jqOHG0sjL6WeN8j0Nv2nvP0UAETpEKx3zZDt2FqDKIdseJLOdKhT"

Document ID from drive fetch api was "1Hk-WnhWbE3yjfx0jKmrtbBN3CJsMtRIsCDauELOma2o"

I am not sure if there is some mismatch or that is how these IDs are intended to work. Anyways I modified your code with this knowledge and it seems to works. (FYI, just like google doc API u will need to enable drive API for this to work)

Please let me know if this helps to achieve what you want or do we need to strictly do it without using drive API

   const { google } = require("googleapis")
    
    const auth = new google.auth.GoogleAuth({
        keyFile: "<path_to_service_account_key_json_file>",
        scopes: [
            "https://www.googleapis.com/auth/documents",
            "https://www.googleapis.com/auth/drive.readonly"
        ]
    })
    
    async function getDocumentContents(documentId) {
        try {
            const authClient = await auth.getClient();
    
            const response = await google.docs({ version: "v1", auth: authClient }).documents.get({ documentId: documentId })
            return response.data;
        } catch (error) {
            console.error(error)
        }
    }
    
    async function getDocumentID() {
        try {
            const authClient = await auth.getClient();
            
            const drive = google.drive({ version: "v3", auth: authClient });
            /* 
            In my case I know that i want to get file by name "Test" else u can use something like -
            q: `name = ${fileName} and mimeType = 'application/vnd.google-apps.document'`
            also u can fetch specific fileds only by passing fields parameter
            fields: 'files(id, name)',
            */
            const specificFileData = await drive.files.list({
                q: `name = "Test" and mimeType = 'application/vnd.google-apps.document'`
            });
    
            return specificFileData.data.files[0].id;
        } catch (error) {
            console.error(error);
        }
    }
    
    (async () => {
        const documentId = await getDocumentID();
        const documentContents = await getDocumentContents(documentId)
        console.log('documentContents', documentContents.body.content)
    })()

1 Comment

Thanks. This seems to be another workaround that requires the published file to be in the user's drive. I prefer the workaround in which we copy the doc since it doesn't use the Google Drive API.
0

Currently Google Docs API does not support retrieving or annotating specific user input or annotations in the document. But you can do this using Google Drive Activity API or Google Drive Revisions API to analyze version history.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.