Skip to content

GoogleDriveHook.get_file_id() constructs Google Drive parent query with reversed "in" operands #72541

Description

@KSchmidAmilar

Under which category would you file this issue?

Providers

Apache Airflow version

2.3.2

What happened and how to reproduce it?

GoogleDriveFileExistenceSensor behaves inconsistently when checking for existing files in a Google Drive folder.

In my case, multiple sensors use the same Google connection, the same folder ID, and the same sensor/helper implementation. Only the filename differs. A sensor for one existing file succeeds, while sensors for other existing files in the same folder repeatedly report that the files do not exist.

For example, the DAG is effectively doing:

from airflow.providers.google.suite.sensors.drive import GoogleDriveFileExistenceSensor

GoogleDriveFileExistenceSensor(
    task_id="check_file",
    folder_id=folder_id,
    file_name=file_name,
)

Looking at GoogleDriveHook.get_file_id(), which is ultimately used by GoogleDriveFileExistenceSensor, the query is currently constructed as:

query = f"name = '{file_name}'"

if folder_id:
    query += f" and parents in '{folder_id}'"

The resulting query is therefore:

name = 'some_file.csv' and parents in 'FOLDER_ID'
However, the Google Drive API documents the in operator as an element-membership operation. The documented syntax for testing whether a folder ID is present in the parents collection is:

'FOLDER_ID' in parents

For example:

name = 'some_file.csv' and 'FOLDER_ID' in parents

There is an additional inconsistency within GoogleDriveHook itself. _ensure_folders_exists() already constructs the parent condition using the documented order:

f```
"'{current_parent}' in parents"

while get_file_id() constructs it in the opposite order:

f"parents in '{folder_id}'"

A minimal test of the two query forms can be performed with the same Airflow connection:

from airflow.providers.google.suite.hooks.drive import GoogleDriveHook

hook = GoogleDriveHook()
service = hook.get_conn()

folder_id = "FOLDER_ID"
file_name = "EXISTING_FILE.csv"

Current Airflow implementation

print(
    hook.get_file_id(
        folder_id=folder_id,
        file_name=file_name,
    )
)

Documented Google Drive query syntax

result = (
    service.files()
    .list(
        q=f"name = '{file_name}' and '{folder_id}' in parents",
        spaces="drive",
        fields="files(id,name,mimeType,parents)",
    )
    .execute()
)

print(result)

The issue is observable through GoogleDriveFileExistenceSensor because GoogleDriveHook.exists() delegates to get_file_id() and interprets an empty result as the file not existing.

This same get_file_id() query-order problem was previously reported in Apache Airflow Discussion #56487, "Suggestion / Hook Improvement: GoogleDriveHook.get_file_id", on October 8, 2025. The reporter specifically identified the same parents in '<folder_id>' expression and reported that changing it to '<folder_id>' in parents resolved their failure to find an existing file.

The problematic expression remains present in the current Google provider implementation.

What you think should happen instead?

GoogleDriveHook.get_file_id() should construct the parent-folder condition using the Google Drive API's documented collection-membership syntax:

if folder_id:
    query += f" and '{folder_id}' in parents"

instead of:

if folder_id:
    query += f" and parents in '{folder_id}'"

This would also make get_file_id() consistent with _ensure_folders_exists() in the same GoogleDriveHook, which already uses:

f"'{current_parent}' in parents"
GoogleDriveFileExistenceSensor should reliably return True for an accessible file whose exact name exists in the specified folder.

Operating System

Ubuntu 22.04 LTS

Deployment

Other

Apache Airflow Provider(s)

google

Versions of Apache Airflow Providers

No response

Official Helm Chart version

Not Applicable

Kubernetes Version

No response

Helm Chart configuration

No response

Docker Image customizations

No response

Anything else?

No response

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:bugThis is a clearly a bugneeds-triagelabel for new issues that we didn't triage yet

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions