Build1 distinct publisher3 min readPublished
Two Apify Actors that work fine alone returned a clean bill of health on eleven sellers nobody had checked, because the field the answer depended on was allowed to arrive empty and the row counts matched all the way down.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Follow any of these and your For You feed starts watching them — no settings page required.
build
One bookkeeping row turns an empty scrape into a billable event1 distinct publisher
build
MCP 2026-07-28 drops the `result` wrapper, and your unit tests will not notice1 distinct publisher
build
An Apify Actor now falsifies its own DNS results before the agent reads them1 distinct publisher
build
MCP's empty-string cursor: the null check that hides most of a tool catalog1 distinct publisher
The second break is the one that travels. The risk Actor takes tax IDs. Hand it an empty string and it behaves correctly: no company found, no risk data, one record out for every record in [17]. Row counts match and the run stays green [5], so everything an orchestrator checks cheaply agrees, and the report says eleven sellers are clean [18].
The two halves are split for good reasons, and the seam sits exactly where those reasons meet. The scraper carries a browser, residential proxies and a container image heavy enough to hold them, while the risk check is plain HTTP against four public sources, so merging them would drag a browser image behind every lookup [8]. They also run on different clocks: seller lists refresh when a client opens a category, risk data is rechecked weekly because bankruptcy filings appear on their own schedule [9]. The downstream Actor is deliberately indifferent to where its IDs came from, which is what makes it reusable and what makes the handoff fragile [20]. The count that would have caught the gap already existed, printed in the run log, and the agent does not read logs [16].
Two conditions have to hold for this failure to recur elsewhere. The upstream tool has to return a field that is legitimately absent sometimes, which the scraper does, since on Wildberries the ID only arrives through a dossier request gated behind fetchDossier and some storefronts have no legal entity at all [15]. And the downstream tool has to treat absence as a valid query rather than an error. If either is false, your chain fails loudly and none of this reaches a client.
The permissions break is narrower and cheaper to apply. sourceDatasetId is a real field and it works on a laptop, where the Python SDK used the author's own token; on the platform a run with standard permissions cannot read another run's default dataset, and the API answers Insufficient permissions [10]. He confirmed that on a cloud run on 25 July, after losing most of a day locally [11]. A local run that passes only confirms the token works; it says nothing about the wiring. The part specific to agents is why that path got tried first: it was the top field in the schema with a confident description [12]. The fix promotes items to the top as the primary input, writes the permissions caveat into its description, and retitles the other field "Source dataset (same-account only)" [13]. Schema order, as the author puts it, is documentation for a reader who stops reading after the first field that works [14].
By his own accounting, a human wiring these two Actors by hand would have hit two of the three breaks [6], which leaves one attributable to the agent doing the wiring [7]. These failure modes predate the Model Context Protocol. What it removed was the reader of the log line, and what it committed to was the first plausible field in a schema [4]. What replaces that reader is a data contract rather than a status code: each seller record now carries a flag stating what a downstream step can do with it [19]. The write-up names three breaks, and the text in front of me documents two, ending on that flag [5]. A boolean in the payload is a cheaper detector than the hand-check of a single company that found the problem in the first place [5].
Ranked by verification strength, evidence, and original report placement.
A client asked which sellers on the marketplaces are risky to buy from, a question that takes two steps: find the sellers behind the product listings, then check each seller's legal entity against public risk registries.
One Actor walks marketplace search results and returns sellers with their tax ID, legal name and store URL; the other takes tax IDs and returns a risk score built from bankruptcy filings, tax arrears, the procurement blacklist and the financial monitoring list.
Both Actors had been in the Apify Store for months and both had paying users.
The agent reached both Actors through the Apify MCP server, which exposes them as tools over the Model Context Protocol; the author says the failures come from how the two Actors hand data to each other, not from which client is used.
Handing the chain to an agent broke it three times and none of the breaks looked like a failure: the runs stayed green, the dataset filled up, and the answer was wrong in a way that only showed up when the author checked a company by hand.
The author states that a human doing the same handoff by hand would have hit two of the three failures.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 2, 2026
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Code-level, single-witness
The specifics are unusually checkable for a war story: an exact API response, a dated cloud run, the schema fragment as shipped, the logging line, the two patches. What none of it has is a second witness. The eleven sellers, the permission wall and the empty-string mapping are all things one engineer saw on his own account, and the piece is written from memory of his own runs rather than from artefacts a reader can pull up.
Two listings, one operator
The only adoption fact anywhere in this reporting is that both Actors sat in the Apify Store for months with paying users — no counts, no names, no revenue. Everything else described is one builder's own pipeline for one client. That is enough to say the components are in commercial use and not enough to say the failure pattern has been met by anyone else.
Sold short
Vendor blogs usually overclaim; this one does the opposite. Apify's post documents its own platform's permission wall, its own schema mistake and its own dataset-enum rejection, and never suggests the fixes generalise. Yet the underlying pattern — a nullable key field plus matching row counts equals a confident wrong answer — is not specific to Actors, tax IDs or Russian registries, and the piece declines to say so. The framing understates its own reach; the one place to hold it to account is the claim that two of three breaks were never about the agent, which is asserted rather than shown.
House organ, arguing against itself
This is published under Apify's own account on dev.to, about Apify's platform, by the seller of the two paid Actors involved — the commercial interest is unhidden and structural. It cuts the other way that the piece's most concrete finding is a limitation of that platform: standard permissions refuse cross-run dataset reads, and the Python SDK's use of a personal token hides it locally. Developer credibility is the payoff here, which happens to reward candour.
Plausible, unreplicated
Internally the account holds together — the mechanism, the error, the two patches and the null-versus-zero reasoning all fit, and the details are too awkward to be invented. But it is one voice, self-reported, on the author's own infrastructure, with an unquantified user base and a truncated third episode. Enough to change how you design a handoff tomorrow; not enough to cite as established platform behaviour.