andy.koh
10/14/2026, 4:03 AMkapa.ai
10/14/2026, 4:03 AM.gz files in the download tab of an asynchronous stream. [GitHub Issue]
The Branch.io cross-events export API likely returns a .csv.gz file as the download target — which matches exactly the scenario in that open issue.
What's Happening The user in that issue configured:
• HTTP Response Format: gzip
• Unzipped data format: CSV
...but still encountered an error during the download phase, even though creation and polling worked fine. This appears to be a known bug in the Connector Builder (as of version 6.48.15) where .gz files cannot be properly read in the download tab. [GitHub Issue]
What You Can Try 1. Verify your Download tab configuration matches the expected format:
◦ HTTP Response Format: gzip
◦ Unzipped data format: CSV
◦ URL field: {{ download_target }} (referencing the URL extracted during polling)
[Download Tab Docs]
1. Check the "Other Requests" panel after testing to confirm that creation and polling are succeeding, and that the download URL is being correctly extracted. [Testing Async Streams]
2. Share your traceback logs (as you mentioned you'll post in thread) — the specific error message will help narrow down whether this is the same .gz reading bug or a different configuration issue.
Current Limitation The knowledge sources indicate this is an open issue with no confirmed fix yet. If you are indeed hitting the .gz download bug, there is currently no documented workaround in the Connector Builder UI for this specific case. You may want to:
• Add a comment or upvote the GitHub issue to signal its impact.
• Consider building a fully custom connector (outside the Builder UI) if you need this to work immediately.
Please share your traceback logs and I can help further diagnose the issue! 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.andy.koh
10/14/2026, 4:04 AMTraceback (most recent call last):
File "/app/airbyte_cdk/sources/streams/concurrent/partition_reader.py", line 79, in process_partition
for record in partition.read():
~~~~~~~~~~~~~~^^
File "/app/airbyte_cdk/sources/declarative/stream_slicers/declarative_partition_generator.py", line 91, in read
for stream_data in self._retriever.read_records(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
self._schema_loader.get_json_schema(), self._stream_slice
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
):
^
File "/app/airbyte_cdk/sources/declarative/retrievers/async_retriever.py", line 91, in read_records
yield from self.record_selector.filter_and_transform(
...<4 lines>...
)
File "/app/airbyte_cdk/sources/declarative/extractors/record_selector.py", line 121, in filter_and_transform
for data in normalized_data:
^^^^^^^^^^^^^^^
File "/app/airbyte_cdk/sources/declarative/extractors/record_selector.py", line 137, in _normalize_by_schema
yield from records
File "/app/airbyte_cdk/sources/declarative/extractors/record_selector.py", line 162, in _transform
for record in records:
^^^^^^^
File "/app/airbyte_cdk/sources/declarative/extractors/record_selector.py", line 154, in _filter
yield from records
File "/app/airbyte_cdk/sources/declarative/async_job/job_orchestrator.py", line 580, in fetch_records
yield from self._job_repository.fetch_records(job)
File "/app/airbyte_cdk/sources/declarative/requesters/http_job_repository.py", line 225, in fetch_records
"polling_response": self._get_polling_response_interpolation_context(job),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
File "/app/airbyte_cdk/sources/declarative/requesters/http_job_repository.py", line 313, in _get_polling_response_interpolation_context
polling_response_context = dict(self._polling_job_response_by_id[job.api_job_id()].json())
ValueError: dictionary update sequence element #0 has length 5; 2 is requiredkapa.ai
10/14/2026, 4:04 AMValueError: dictionary update sequence element #0 has length 5; 2 is required
This happens in http_job_repository.py when the CDK tries to build the polling_response interpolation context by calling .json() on the polling response and converting it to a dict. The error suggests that the polling response body is not a valid JSON object (key-value pairs) — instead, it appears to be returning something like a list of strings or a non-dict-compatible structure (e.g., a list where the first element has length 5, not 2 key-value pairs).
Likely Root Cause The Branch.io polling response may be returning a JSON array or some other non-object structure at the top level, rather than a JSON object ({}). The CDK expects the polling response to be a dictionary, so it can be used for interpolation via {{ polling_response }}.
What to Check 1. Inspect your polling response using the "Other Requests" panel in the Connector Builder UI after a test run. Look at the raw response body from the polling request. [Testing Async Streams]
2. Verify the Status Extractor field path is correctly pointing into the polling response structure. If the response is nested or array-based, the field path needs to reflect that. [Polling Tab Docs]
3. Verify the Download Target Extractor field path is correctly pointing to the download URL within the polling response. [Polling Tab Docs]
Important Note This error is separate from the .gz download bug mentioned in the GitHub issue. This error is crashing before the download phase even begins — it fails while trying to prepare the polling_response context to construct the download request. So resolving this error is a prerequisite before you'd encounter any .gz-related issues. [GitHub Issue]
Could you share what your polling response body looks like from the "Other Requests" panel? That would help pinpoint the exact misconfiguration. 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.andy.koh
10/14/2026, 4:10 AM[
{
"job_id": "redacted",
"created_date": "2026-10-13 12:24:12.913814",
"completion_date": "2026-10-13 12:32:19.789",
"status": "SUCCEEDED",
"download_links": [
"<https://branch-exports-web-usw2.s3.us-west-2.amazonaws.com/branch_agg_async_api/y%3D2026/m%3D10/d%3D13/app_id%3D644074824343244968/job_id%3Dredacted/task_id%3D0/filename-1.csv.gz>",
"<https://branch-exports-web-usw2.s3.us-west-2.amazonaws.com/branch_agg_async_api/y%3D2026/m%3D10/d%3D13/app_id%3D644074824343244968/job_id%3Dredacted/task_id%3D0/filename-2.csv.gz>"
]
}
]kapa.ai
10/14/2026, 4:10 AM[...]) rather than a JSON object ({...}). The CDK's http_job_repository.py calls .json() and tries to convert it to a dict, but since it's a list, it fails with:
ValueError: dictionary update sequence element #0 has length 5; 2 is required
The "length 5" refers to the string "job_id" — the first key of the first object in the array — which has 5 characters, not the 2 elements (key, value) that dict() expects.
The Problem The CDK expects the polling response to be a top-level JSON object ({}), but Branch.io returns a top-level JSON array ([]) containing one object.
What to Try Unfortunately, the knowledge sources do not explicitly document a built-in way to handle a top-level array polling response in the Connector Builder's async stream configuration. [Polling Tab Docs]
However, here are some things worth checking:
1. Status Extractor field path: Try setting the field path to [0]/status or 0/status to index into the first element of the array, rather than just status. This may or may not resolve the underlying dict() conversion issue, as that happens before field extraction.
2. Download Target Extractor field path: Similarly, try [0]/download_links or 0/download_links to point to the array of URLs.
3. Multiple download links: Your polling response also returns two .csv.gz download URLs in the download_links array. The knowledge sources do not document how the Connector Builder handles multiple download URLs from a single job — this may be an additional limitation to be aware of. [Download Tab Docs]
Recommendation Given these two compounding issues:
• Top-level array in polling response (not supported by CDK's dict() conversion)
• Multiple .gz download URLs per job
• The existing open bug with .gz file reading [GitHub Issue]
This connector may be beyond what the Connector Builder currently supports. You may need to build a fully custom Python connector using the Airbyte CDK directly, where you have full control over how the polling response is parsed and how multiple download URLs are iterated.
I'd also recommend filing or commenting on a GitHub issue describing the top-level array polling response limitation, as it appears to be an unsupported edge case in the current CDK implementation. 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.andy.koh
10/15/2026, 1:27 AMstatus seem to have worked. Trying [0]/status or 0/status would have kept the pipeline hanging as it is unable to find the status.
2. Download Target Extractor field path: [0]/download_links or 0/download_links would avoid that ValueError failure but there is no record being fetched
Any further advice you can provide on what we could try?kapa.ai
10/15/2026, 1:27 AMstatus works for the Status Extractor (the CDK is somehow resolving it from the array's first element)
• [0]/download_links avoids the ValueError but returns no records
• The download links are an array of multiple .csv.gz URLs
The Core Remaining Problems There are likely two compounding issues at this point:
1. Multiple Download URLs Your download_links field is itself an array of URLs:
"download_links": [
"https://...filename-1.csv.gz",
"https://...filename-2.csv.gz"
]
The Download Target Extractor is designed to extract a single download URL or identifier. The knowledge sources do not document support for iterating over multiple download URLs from a single polling response. [Download Tab Docs]
2. The .gz File Reading Bug Even if the download URL is correctly extracted, there is an open bug where the Connector Builder fails to read .csv.gz files in the download tab. [GitHub Issue]
What You Could Still Try • Try setting the Download Target Extractor field path to [0]/download_links/0 to target just the first URL in the array. This would at least isolate whether the no-records issue is caused by the multi-URL array or the .gz reading bug.
Honest Assessment The knowledge sources do not contain enough information to confirm a working solution for your specific case. Given the combination of:
1. Top-level array polling response
2. Multiple .gz download URLs per job
3. The open .gz reading bug in the Connector Builder
...this connector is very likely beyond what the Connector Builder currently supports reliably. Building a fully custom Python connector using the Airbyte CDK directly is probably the most viable path forward at this point, as it would give you full control over array parsing, URL iteration, and .gz decompression. 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.andy.koh
10/15/2026, 1:30 AMkapa.ai
10/15/2026, 1:30 AMRecordExpander component expands records by extracting items from a nested array field, emitting each item as a separate record. It supports wildcards (*) for matching multiple arrays. [YAML Reference]
For example, if a record contains:
{
"download_links": [
"url-1.csv.gz",
"url-2.csv.gz"
]
}
With expand_records_from_field: ["download_links"], it would emit two separate records — one per URL.
Would It Help Your Case? Potentially partially, but with caveats:
• For the multiple download URLs problem: Record Expander could theoretically expand your download_links array so that each URL is treated as a separate record/target. This directly addresses the issue of having multiple .csv.gz URLs per job. [YAML Reference]
• Useful options to consider:
◦ merge_parent: true — merges parent record fields (like job_id, status) into each expanded item, so you retain context per URL
◦ on_no_records — controls behavior if download_links is missing or empty (skip or emit_parent)
Important Caveats The knowledge sources do not explicitly document how Record Expander behaves specifically within the Download Target Extractor context of an async stream. It's unclear whether the expanded items would each be used as separate download targets or just as separate output records.
Additionally, the .gz file reading bug remains an open issue regardless of how the download URLs are extracted. [GitHub Issue]
Recommendation It's worth trying — configure the Record Expander with:
• expand_records_from_field: ["download_links"] (or [0]/download_links given your top-level array response)
• merge_parent: true to retain job metadata on each expanded record
Then test and observe whether records are returned. This would at least help isolate whether the no-records issue is the multi-URL array problem or the .gz reading bug. 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.kwong
10/15/2026, 3:26 AMdownload_links --> Error: "ValueError
dictionary update sequence element #0 has length 5; 2 is required"
Field Path: 0/download_links or [0]/download_links --> Test passes, but connection get no records loaded
And we tried using Epland Records From Field instead,
Expand Recrods From Field: download_links --> Error: "ValueError dictionary update sequence element #0 has length 5; 2 is required"
Expand Recrods From Field: 0/download_links or [0]/download_links --> Test passes, but connection get no records loadedkapa.ai
10/15/2026, 3:26 AM.csv.gz files.
Most Likely Remaining Cause This points squarely at the open .gz file reading bug in the Connector Builder. Even when the download URL is correctly resolved, the Connector Builder cannot successfully decompress and parse the .csv.gz content, resulting in zero records rather than an explicit error. [GitHub Issue]
Honest Conclusion The knowledge sources do not contain enough information to provide a working workaround for this within the Connector Builder UI. Based on everything tested, you have hit two hard limitations:
1. The top-level array polling response requiring the 0/ prefix workaround
2. The .gz download reading bug preventing any records from loading
Recommended Path Forward Given these findings, I would strongly recommend:
1. File a GitHub issue (or comment on the existing one) with your specific Branch.io findings — the top-level array polling response is a distinct additional bug worth reporting separately.
2. Build a custom Python connector using the Airbyte CDK directly, where you have full control over:
◦ Parsing the top-level array polling response
◦ Iterating over multiple download URLs
◦ Manually handling .gz decompression
The Connector Builder does not currently appear to support your specific combination of API response structures reliably. 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help., with interactive elementskwong
10/15/2026, 3:32 AM[0]/download_links for expand records from field is:
2026-10-15 14:21:36 replication-orchestrator INFO ----- START REPLICATION -----
2026-10-15 14:21:36 replication-orchestrator INFO
2026-10-15 14:21:36 replication-orchestrator INFO Running destination...
2026-10-15 14:21:36 replication-orchestrator INFO Writing messages to protocol version 0.2.0
2026-10-15 14:21:36 replication-orchestrator INFO Reading messages from protocol version 0.2.0
2026-10-15 14:21:36 replication-orchestrator INFO Closing io.micronaut.context.propagation.instrument.execution.ContextPropagatingExecutorService@7951a08c
2026-10-15 14:21:36 replication-orchestrator INFO Closing io.micronaut.context.propagation.instrument.execution.ContextPropagatingExecutorService@7e168d90
2026-10-15 14:21:36 replication-orchestrator INFO Closing io.airbyte.container.orchestrator.worker.io.LocalContainerAirbyteDestination@cf0e8b4
2026-10-15 14:21:36 destination ERROR io.airbyte.cdk.TransientErrorException: Input was fully read, but some streams did not receive a terminal stream status message. If the destination did not encounter other errors, this likely indicates an error in the source or platform. Streams without a status message: [kw_branch_io_cross_event_new]
2026-10-15 14:21:36 destination ERROR at io.airbyte.cdk.load.state.SyncManager.markInputConsumed(SyncManager.kt:116)
2026-10-15 14:21:36 destination ERROR at io.airbyte.cdk.load.state.PipelineEventBookkeepingRouter.close(PipelineEventBookkeepingRouter.kt:313)
2026-10-15 14:21:36 destination ERROR at io.airbyte.cdk.load.task.internal.InputConsumerTask.execute(InputConsumerTask.kt:117)
2026-10-15 14:21:36 destination ERROR at io.airbyte.cdk.load.task.DestinationTaskLauncher$WrappedTask.execute(DestinationTaskLauncher.kt:124)
2026-10-15 14:21:36 destination ERROR at io.airbyte.cdk.load.task.TaskScopeProvider$launch$job$1.invokeSuspend(TaskScopeProvider.kt:35)
2026-10-15 14:21:36 destination ERROR at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:124)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:820)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:717)
2026-10-15 14:21:36 destination ERROR at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:704)
2026-10-15 14:21:37 replication-orchestrator ERROR Error closing resource io.airbyte.container.orchestrator.worker.io.LocalContainerAirbyteDestination@cf0e8b4; recording failure but continuing.
2026-10-15 14:21:37 replication-orchestrator INFO Closing io.airbyte.container.orchestrator.worker.io.LocalContainerAirbyteSource@4c14ed24
2026-10-15 14:21:37 replication-orchestrator INFO Closing io.airbyte.container.orchestrator.worker.RecordSchemaValidator@26db7540
2026-10-15 14:21:37 replication-orchestrator INFO Closing io.airbyte.container.orchestrator.persistence.SyncPersistenceImpl@4dcdc172
2026-10-15 14:21:37 replication-orchestrator ERROR Killing orchestrator because of an Exceptionkapa.ai
10/15/2026, 3:32 AMio.airbyte.cdk.TransientErrorException: Input was fully read, but some streams did not
receive a terminal stream status message.
Streams without a status message: [kw_branch_io_cross_event_new]
What This Means This is a separate, known bug where source-declarative-manifest does not emit terminal STREAM_STATUS messages required by CDK v3 destinations. [GitHub Issue]
Critically, this error is happening at the destination level, not the source level. It means:
• The source did complete reading (input was fully read)
• But the destination never received the required terminal stream status message
• This is unrelated to the .gz or field path issues we were debugging earlier
What This Tells Us About Your Previous Tests The "zero records" results you saw earlier may actually have been caused by this same terminal status bug silently failing the sync, rather than a .gz reading problem. The .gz bug may not be the blocker after all.
Current Workarounds (from the GitHub issue) [GitHub Issue]
1. Downgrade your destination to a non-CDK v3 version:
◦ BigQuery → v2.x
◦ S3 → v0.6.7 or earlier (pre-v1.0.0)
1. Build a custom Python connector using the traditional Python CDK, which properly emits STREAM_STATUS messages and works with any destination version.
Important Context • This bug has been open since at least June 2025
• It was temporarily resolved by a code revert but the permanent fix was never implemented
• It affects all source-declarative-manifest connectors (including Connector Builder-based connectors) paired with CDK v3 destinations
• There is no YAML-level configuration option to force stream status emission
Recommended Next Steps 1. First, try downgrading your destination to a non-CDK v3 version to confirm this is the primary blocker — this will tell you whether the .gz reading and field path issues are also real blockers or were masked by this error all along.
2. If downgrading resolves the terminal status error and records still don't load, then the .gz issue is a real secondary blocker.
3. If downgrading resolves everything, you have a working connector but are pinned to an older destination version until Airbyte ships a permanent fix. 👍 0 button 👎 0 button ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.