ToolJet/ToolJet · error · QueryError

Could not fetch tables

Error message

Could not fetch tables

What it means

Thrown by _listAllTables (the backing implementation of invokeMethod('listTables')) when the AWS SDK's ListTablesCommand.send rejects inside the pagination loop. The loop pages through all table names using LastEvaluatedTableName with a Limit of 100, so any send failure — on any page — aborts and is wrapped as 'Could not fetch tables'. This feeds the table-picker dropdown in the editor.

Source

Thrown at plugins/packages/dynamodb/lib/index.ts:165

    throw new QueryError('Method not found', `Method '${methodName}' is not supported by the DynamoDB plugin`, {});
  }

  private async _listAllTables(
    client: DynamoDBClient,
    args?: any
  ): Promise<{ items: Array<{ value: string; label: string }>; totalCount: number }> {
    const tables: string[] = [];
    let lastEvaluatedTableName: string | undefined;

    try {
      do {
        const command = new ListTablesCommand({ ExclusiveStartTableName: lastEvaluatedTableName, Limit: 100 });
        const data = await client.send(command);
        tables.push(...(data.TableNames || []));
        lastEvaluatedTableName = data.LastEvaluatedTableName;
      } while (lastEvaluatedTableName);
    } catch (err) {
      throw new QueryError('Could not fetch tables', err.message, {});
    }

    const search = (args?.search || '').toLowerCase();
    const filtered = search ? tables.filter((name) => name.toLowerCase().includes(search)) : tables;

    const page = args?.page || 1;
    const limit = args?.limit;

    if (limit) {
      const start = (page - 1) * limit;
      return {
        items: filtered.slice(start, start + limit).map((name) => ({ value: name, label: name })),
        totalCount: filtered.length,
      };
    }

    return {
      items: filtered.map((name) => ({ value: name, label: name })),

View on GitHub (pinned to 20602a8e10)

Solutions

  1. Grant the credential's principal dynamodb:ListTables on '*' (or at minimum the account scope) — this action is account-scoped, not table-scoped.
  2. Verify the region in the datasource config is the region where your tables live.
  3. If using instance-profile or ARN-role credentials, confirm the role trust policy and session are still valid (re-test the connection).
  4. Read the wrapped err.message for the specific AWS error code (AccessDeniedException vs. ThrottlingException) and act on it.

Example fix

// before — IAM policy missing ListTables
{
  "Effect": "Allow",
  "Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
  "Resource": "arn:aws:dynamodb:us-east-1:123:table/*"
}
// after — add account-scoped ListTables
{
  "Effect": "Allow",
  "Action": ["dynamodb:ListTables"],
  "Resource": "*"
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify IAM has ListTables before opening the picker
// (no in-process validation; this is a server-side permission check)

Try / catch

try { await plugin.invokeMethod('listTables', ctx, sourceOptions, args); }
catch (e) { if (e instanceof QueryError && e.message === 'Could not fetch tables') { /* show 'check dynamodb:ListTables permission' */ } else throw e; }

Prevention

When it happens

Trigger: The configured IAM principal lacks dynamodb:ListTables on the account/global resource. Network failure or STS/region misconfiguration reaching the DynamoDB endpoint. Throttling from excessive ListTables calls. An instance-profile or assumed-role credential that expired mid-iteration. The region in sourceOptions points at a region with no access.

Common situations: Datasource created with an IAM user that has table-level grants but not the account-level ListTables permission. EC2 instance profile credentials rotated/expired. Wrong region selected so the endpoint is unreachable or returns AccessDenied. Heavy concurrent editing sessions hammering ListTables.

Related errors


AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13). Data as JSON: /api/errors/33eff4c3be12155a. Report an issue: GitHub.