MyCATApache/Mycat-Server · error · SQLNonTransientException

parent key can't find any valid datanode

Error message

parent key can't find any valid datanode 

What it means

Thrown by RouterUtil when routing a multi-table SQL statement: one of the sharded tables' route calculation produced an empty datanode set, so no valid data node exists to send the statement to. Mycat requires at least one target datanode per table in tablesRouteMap to build a RouteResultset.

Solutions

  1. Check rule.xml for the table's sharding algorithm and ensure the condition value maps to a valid datanode (add covering ranges/default node)
  2. Verify the WHERE clause value on the partition column is in the expected format (dates, numeric strings) that the algorithm accepts
  3. Log and inspect calculate() result of the partition function for the offending value
  4. Simplify the statement to a single table to confirm which table's route is empty

Example fix

// before (rule.xml missing coverage for value)
<function name="sharding-by-range" class="io.mycat.route.function.AutoPartitionByLong">
  <property name="mapping">0-1000=0</property>
</function>
// after (add range or default node covering remaining values)
<function name="sharding-by-range" class="io.mycat.route.function.AutoPartitionByLong">
  <property name="mapping">0-1000=0\n1001-2000=1\ndefault=0</property>
</function>
Defensive patterns

Strategy: validation

Validate before calling

// Java: before issuing multi-table SQL, pre-check each table's route
for (String table : tables) {
    TableConfig tc = schema.getTables().get(table.toUpperCase());
    if (tc == null || tc.getDataNodes().isEmpty()) throw new IllegalStateException("no route for " + table);
}

Type guard

if (tc == null || tc.getDataNodes() == null || tc.getDataNodes().isEmpty()) { return false; }

Try / catch

try { rrs = route(...); } catch (SQLNonTransientException e) { if (e.getMessage().contains("valid datanode")) { LOG.warn("empty route, check rule.xml coverage", e); } throw e; }

Prevention

When it happens

Trigger: Executing an UPDATE/DELETE/SELECT involving multiple tables where a WHERE condition on the partition column yields no matching datanode (e.g. a partition algorithm returns null or an empty route for the given column value), or the table route set ends up empty.

Common situations: Sharding function configured with rules that don't cover all possible values (e.g. mod/hash rules missing ranges); queries with conditions on a partition column value outside any configured shard range; stale or wrong rule.xml partition configuration.

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


AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11). Data as JSON: /api/errors/7b568df6f267f322. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/io/mycat/route/util/RouterUtil.java:1312

		}

		Set<String> retNodesSet = new HashSet<String>();

		//分库解析信息不为空
		Map<String, Map<String, Set<ColumnRoutePair>>> tablesAndConditions = routeUnit.getTablesAndConditions();
		if(tablesAndConditions != null && tablesAndConditions.size() > 0) {
			//为分库表找路由
			RouterUtil.findRouteWithcConditionsForTables(schema, rrs, tablesAndConditions, tablesRouteMap, ctx.getSql(), cachePool, isSelect);
			if(rrs.isFinishedRoute()) {
				return rrs;
			}
		}


		boolean isFirstAdd = true;
		for(Map.Entry<String, Set<String>> entry : tablesRouteMap.entrySet()) {
			if(entry.getValue() == null || entry.getValue().size() == 0) {
				throw new SQLNonTransientException("parent key can't find any valid datanode ");
			} else {
				if(isFirstAdd) {
					retNodesSet.addAll(entry.getValue());
					isFirstAdd = false;
				} else {
					retNodesSet.retainAll(entry.getValue());
					if(retNodesSet.size() == 0) {//两个表的路由无交集
						String errMsg = "invalid route in sql, multi tables found but datanode has no intersection "
								+ " sql:" + ctx.getSql();
						LOGGER.warn(errMsg);
						throw new SQLNonTransientException(errMsg);
					}
				}
			}
		}

		if(retNodesSet != null && retNodesSet.size() > 0) {
			String tableName = tables.get(0);

View on GitHub (pinned to 65f8d8beb7)