You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: BigQueryIO.read() requires GCS tempLocation when using BigQuery Emulator #35777
When using the BigQuery emulator in combination with BigQueryIO.read() in Apache Beam (Java), the pipeline fails with the following error when running locally with DirectRunner:
java.lang.IllegalArgumentException: BigQueryIO.Read needs a GCS temp location to store temp files. This can be set with option --tempLocation.
This makes it impossible to write integration tests against the emulator using BigQueryIO.read() unless a (fake) GCS-compatible path is provided—even though the emulator does not support or require real GCS interaction.
This is WAI. BigQueryIO.read() default to EXPORT, where Beam does not read from BigQuery directly. It invokes BigQuery export API to export tables to gcs, then Beam just read GCS files.
Have you tried BigQueryIO.read with DIRECT_READ method (which does not require tempLocation)?
I have also try with DIRECT_READ and face another issue.
The BigQuery job completes successfully, but the Apache Beam pipeline remains stuck, beam is likely waiting for the results to be available in the temporary table
This issue has been marked as stale due to 150 days of inactivity. It will be closed in 30 days if no further activity occurs. If you think that’s incorrect or this issue still needs to be addressed, please simply write any comment. If closed, you can reopen the issue at any time. Thank you for your contributions.
@Abacn thanks for reopening this. I reproduced it against ghcr.io/goccy/bigquery-emulator and the DIRECT_READ half that @hichmo raised in the follow-up looks like a real gap, distinct from the tempLocation part.
The tempLocation half is WAI, as you said.BigQueryIO.java:1415 already skips that check when the method is DIRECT_READ, so nothing needs to change there:
if (getMethod() != TypedRead.Method.DIRECT_READ) {
StringtempLocation = bqOptions.getTempLocation();
checkArgument(!Strings.isNullOrEmpty(tempLocation), "BigQueryIO.Read needs a GCS temp location ...");
Why DIRECT_READ hangs
StorageClientImpl honors --bigQueryEndpoint but keeps the default gRPC transport, which negotiates TLS (BigQueryServicesImpl.java:1902-1913):
The emulator logged nothing at all for the TLS attempt, i.e. the request never got past the handshake. The plaintext row is the control: the emulator's Storage Read API itself works.
In a real pipeline this is worse than the 25s above suggests. BigQueryServicesImpl.java:1918-1925 sets createReadSession's initial, max and total RPC timeout to Duration.ofHours(2), so the user-visible symptom is a silent two-hour stall rather than an error. That matches @hichmo's report that the BigQuery job completes but the pipeline stays stuck.
newBigQueryWriteClient (BigQueryServicesImpl.java:1786-1789) has the same gap, so STORAGE_WRITE_API against an emulator is blocked in the same way.
A second, independent blocker: one option, two ports
Running an actual DIRECT_READ pipeline never even reaches the storage client. With --bigQueryEndpoint=http://localhost:9060/ (the value the gRPC client needs), the REST client is pointed at the gRPC port and hangs there first:
bigQueryEndpoint feeds three clients with incompatible requirements: the REST client takes it verbatim as setRootUrl and needs http://host:9050/, while both gRPC clients run it through trimSchemaIfNecessary (BigQueryServicesImpl.java:1803) and need host:9060. The emulator serves REST on 9050 and gRPC on 9060, so no single value works. Fixing plaintext alone would not be enough.
Precedent already in this module
FirestoreStatefulComponentFactory.java:86-97 solves exactly this, in the same package tree:
SpannerAccessor.java:275-277 does the same behind a usePlaintext flag.
One question before I write anything
Any fix here has to add public surface to BigQueryOptions, and that is the part I did not want to guess at. What reads most naturally to me is a new getBigQueryEmulatorHost(): it would point the two gRPC clients at that host with plaintext and a no-op credential, and leave bigQueryEndpoint alone for the REST client. That follows the Firestore precedent above, and since it is purely additive nothing changes for anyone already setting bigQueryEndpoint. The other way to do it would be a separate gRPC endpoint option alongside an explicit plaintext flag, which is closer to the Spanner shape.
Do you have a preference? I have the reproduction set up and am happy to send a PR either way. Not claiming the issue in the meantime.
Setup for all of the above: Beam 2.75.0 on the DirectRunner, emulator seeded with a single three-row table.
One note if you try to reproduce this on 2.75.0. Before reaching either of the blockers above you will hit an NPE in BigQueryStorageTableSource.getEstimatedSizeBytes, because the emulator returns no numBytes for the table. That one is unrelated to this issue and is already fixed on master by 4e5ae91.
Activity
Abacn commentedon Aug 8, 2025
This is WAI. BigQueryIO.read() default to EXPORT, where Beam does not read from BigQuery directly. It invokes BigQuery export API to export tables to gcs, then Beam just read GCS files.
Have you tried BigQueryIO.read with DIRECT_READ method (which does not require tempLocation)?
hichmo commentedon Aug 8, 2025
I have also try with DIRECT_READ and face another issue.
The BigQuery job completes successfully, but the Apache Beam pipeline remains stuck, beam is likely waiting for the results to be available in the temporary table
github-actions commentedon Jan 6, 2026
This issue has been marked as stale due to 150 days of inactivity. It will be closed in 30 days if no further activity occurs. If you think that’s incorrect or this issue still needs to be addressed, please simply write any comment. If closed, you can reopen the issue at any time. Thank you for your contributions.
github-actions commentedon Feb 5, 2026
This issue has been closed due to lack of activity. If you think that is incorrect, you can reopen the issue at any time.
mxtymoshyk commentedon Aug 31, 2026
@Abacn thanks for reopening this. I reproduced it against
ghcr.io/goccy/bigquery-emulatorand theDIRECT_READhalf that @hichmo raised in the follow-up looks like a real gap, distinct from thetempLocationpart.The
tempLocationhalf is WAI, as you said.BigQueryIO.java:1415already skips that check when the method isDIRECT_READ, so nothing needs to change there:Why DIRECT_READ hangs
StorageClientImplhonors--bigQueryEndpointbut keeps the default gRPC transport, which negotiates TLS (BigQueryServicesImpl.java:1902-1913):The emulator's gRPC port is plaintext, so the handshake never completes. A/B with the same client, same endpoint, only the transport differing:
InstantiatingGrpcChannelProvider+setChannelConfigurator(c -> c.usePlaintext())streams=1BigQueryReadSettings.defaultGrpcTransportProviderBuilder()(what Beam uses)Parked here:
The emulator logged nothing at all for the TLS attempt, i.e. the request never got past the handshake. The plaintext row is the control: the emulator's Storage Read API itself works.
In a real pipeline this is worse than the 25s above suggests.
BigQueryServicesImpl.java:1918-1925setscreateReadSession's initial, max and total RPC timeout toDuration.ofHours(2), so the user-visible symptom is a silent two-hour stall rather than an error. That matches @hichmo's report that the BigQuery job completes but the pipeline stays stuck.newBigQueryWriteClient(BigQueryServicesImpl.java:1786-1789) has the same gap, soSTORAGE_WRITE_APIagainst an emulator is blocked in the same way.A second, independent blocker: one option, two ports
Running an actual
DIRECT_READpipeline never even reaches the storage client. With--bigQueryEndpoint=http://localhost:9060/(the value the gRPC client needs), the REST client is pointed at the gRPC port and hangs there first:bigQueryEndpointfeeds three clients with incompatible requirements: the REST client takes it verbatim assetRootUrland needshttp://host:9050/, while both gRPC clients run it throughtrimSchemaIfNecessary(BigQueryServicesImpl.java:1803) and needhost:9060. The emulator serves REST on 9050 and gRPC on 9060, so no single value works. Fixing plaintext alone would not be enough.Precedent already in this module
FirestoreStatefulComponentFactory.java:86-97solves exactly this, in the same package tree:SpannerAccessor.java:275-277does the same behind ausePlaintextflag.One question before I write anything
Any fix here has to add public surface to
BigQueryOptions, and that is the part I did not want to guess at. What reads most naturally to me is a newgetBigQueryEmulatorHost(): it would point the two gRPC clients at that host with plaintext and a no-op credential, and leavebigQueryEndpointalone for the REST client. That follows the Firestore precedent above, and since it is purely additive nothing changes for anyone already settingbigQueryEndpoint. The other way to do it would be a separate gRPC endpoint option alongside an explicit plaintext flag, which is closer to the Spanner shape.Do you have a preference? I have the reproduction set up and am happy to send a PR either way. Not claiming the issue in the meantime.
Setup for all of the above: Beam 2.75.0 on the DirectRunner, emulator seeded with a single three-row table.
One note if you try to reproduce this on 2.75.0. Before reaching either of the blockers above you will hit an NPE in
BigQueryStorageTableSource.getEstimatedSizeBytes, because the emulator returns nonumBytesfor the table. That one is unrelated to this issue and is already fixed on master by 4e5ae91.