apache/beam · error · IllegalArgumentException
TVF specified: + tvfName + is not found in the existing…
Error message
TVF specified: + tvfName + is not found in the existing TVF's: + foundNames
What it means
When SpannerIO is configured with a TVF (table-valued function) query, it queries INFORMATION_SCHEMA for the available TVFs and throws IllegalArgumentException if the requested tvfName is not among the found names. This fails fast at pipeline construction/expansion rather than at query time with a Spanner error.
Solutions
- Compare the tvfName in the message against the listed foundNames and fix the spelling/casing.
- Run SELECT tvf_name FROM INFORMATION_SCHEMA.TVFs (per dialect) against the configured database to list actual TVFs.
- Verify instanceId/databaseId in SpannerConfig point to the database where the TVF exists.
- Ensure quoting matches Spanner's rules (backticks for GoogleSQL) so the escaped name matches exactly.
Example fix
// before
SpannerRead.of(config).withQuery("SELECT * FROM TVF tvf_users(...)") // TVF 'tvf_users' typo
// after
SpannerRead.of(config).withQuery("SELECT * FROM TVF tvf_user_events(...)") Defensive patterns
Strategy: validation
Validate before calling
// Before building the pipeline, verify the TVF exists
// SELECT tvf_name FROM INFORMATION_SCHEMA.TVFs — check tvfName is present
Set<String> tvfs = fetchTvfs(spannerConfig);
if (!tvfs.contains(tvfName)) throw new IllegalArgumentException("TVF missing: " + tvfName); Try / catch
try { pipeline.apply(SpannerRead.of(config).withQuery(tvfSql)); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("TVF specified")) { /* correct name or deploy TVF */ } throw e; } Prevention
- List existing TVFs with INFORMATION_SCHEMA.TVFs before referencing one.
- Keep TVF DDL migrations applied to every environment the pipeline targets.
- Match Spanner quoting rules exactly (backticks for GoogleSQL identifiers).
When it happens
Trigger: Using SpannerRead.withQuery()/SpannerQuerySourceDef with a table-valued function whose quoted name is not present in the database's INFORMATION_SCHEMA.TVF list at pipeline build time.
Common situations: Typo in the TVF name; TVF created in a different database/project/instance; quoting differences (backticks/case) making the names not match; TVF dropped or not yet deployed to the target environment (e.g., pointing a staging pipeline at prod DB).
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Cannot find Spanner table.
- Exception while trying to retrieve schema
- Exception while trying to retrieve schema
- Invalid ARRAY type: + originalSpannerType
- Null collection element type at field
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/d095267ddf099e16.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerIO.java:3083
}
sql.append(")");
Statement.Builder builder = Statement.newBuilder(sql.toString());
for (int i = 0; i < quoteEscapedTvfNameList.size(); i++) {
if (dialect == Dialect.POSTGRESQL) {
builder.bind("p" + (i + 1)).to(quoteEscapedTvfNameList.get(i));
} else {
builder.bind("p" + i).to(quoteEscapedTvfNameList.get(i));
}
}
Statement statement = builder.build();
ResultSet resultSet = tx.executeQuery(statement);
java.util.Set<String> foundNames = new java.util.HashSet<>();
while (resultSet.next()) {
foundNames.add(resultSet.getString(0));
}
for (String tvfName : quoteEscapedTvfNameList) {
if (!foundNames.contains(tvfName)) {
throw new IllegalArgumentException(
"TVF specified: " + tvfName + " is not found in the existing TVF's: " + foundNames);
}
}
}
}
@VisibleForTesting
static boolean isMutableChangeStream(
DatabaseClient databaseClient, Dialect dialect, String changeStreamName) {
String fetchedPartitionMode = fetchPartitionMode(databaseClient, dialect, changeStreamName);
if (fetchedPartitionMode.isEmpty()
|| fetchedPartitionMode.equalsIgnoreCase("IMMUTABLE_KEY_RANGE")) {
return false;
}
return true;
}
private static String fetchPartitionMode(View on GitHub (pinned to 12126d8942)