Troubleshooting & FAQ
Diagnose and fix the issues TableCrafter users hit most often — a source that will not load, browser CORS errors, empty or column-less tables, stale cached data, and Elementor/Gutenberg editor quirks. Every fix below maps to real plugin behavior, error strings, and tooling.
How to read TableCrafter errors
TableCrafter shows two different error experiences depending on who is viewing the page. This is the first thing to understand when something is "broken."
- Administrators (users with the
manage_optionscapability) see a detailed TableCrafter Setup Guide box — a dashed red panel that prints the exact underlying error (for example a path or HTTP error) plus troubleshooting tips. This helper is only ever shown to admins. - Visitors and other logged-in users see a neutral fallback: "Unable to load data. Please check back later." Raw PHP warnings and internal error strings are never exposed to the public.
If a table looks fine for you but a client reports it is empty, log in as an admin on the same page. The admin-only Setup Guide will reveal the real reason the fetch failed.
Source unreachable or blocked by SSRF protection
TableCrafter runs every remote source URL through an SSRF (Server-Side Request Forgery) check before fetching. The check uses WordPress core's wp_http_validate_url(), which rejects localhost, loopback addresses, and private/reserved IP ranges. When a URL is rejected you will see:
The provided URL is blocked for safety (Local/Private IP).
Common causes and fixes:
| Symptom | Cause & Fix |
|---|---|
| "blocked for safety" on a localhost URL | Pointing at http://localhost, 127.0.0.1, or a private LAN IP (10.x, 192.168.x, etc.) is intentionally refused. Expose the data on a public hostname instead. |
| "Source returned HTTP 403/404/500" | The source itself answered with an error code. Open the URL directly in a browser. Fix the endpoint, authentication, or path. TableCrafter only treats HTTP 200 as success. |
| "CURL Error: ..." | Connection-level failure (DNS, timeout, or TLS). The fetcher uses a 10s connect / 30s total timeout with SSL verification on. Confirm the host resolves and presents a valid certificate. |
| "The source did not return a valid JSON structure." | The endpoint replied with HTML, XML, or malformed JSON. For spreadsheets, use a public Google Sheet URL or a .csv link, which are parsed differently. |
Reverse proxies and CDNs can rewrite your visitor's IP. If your rate-limiting or logging needs to trust a proxy header (e.g. Cloudflare's CF-Connecting-IP), opt in explicitly with the tablecrafter_trusted_ip_headers filter — TableCrafter ignores proxy headers by default to prevent IP spoofing.
// Trust Cloudflare's connecting-IP header (only if behind Cloudflare).
add_filter( 'tablecrafter_trusted_ip_headers', function () {
return [ 'cloudflare' ]; // also: 'forwarded', 'real_ip'
} );
CORS errors in the browser console
If you previously fetched data client-side and saw a "blocked by CORS policy" error, that is exactly the problem TableCrafter's server-side proxy solves. When a table renders, your server fetches the remote data first, so the browser never makes a cross-origin request and CORS never applies.
The frontend script reaches the proxy through WordPress AJAX:
- Action:
tc_proxy_fetch(handled for both logged-in andnoprivrequests) - Nonce action:
tc_proxy_nonce - Authorization: caller must have
edit_postsormanage_options
If the proxy itself returns an error, check these in order:
| Response | Meaning & Fix |
|---|---|
| HTTP 429 / "Rate limit exceeded" | The proxy allows 30 requests per 60-second window per user (or per IP for anonymous callers). Reduce aggressive auto_refresh intervals or the number of live tables on one page. |
| "Unauthorized: You do not have permission..." | The requesting user lacks edit_posts. This typically affects anonymous-only flows; for public tables, rely on the server-rendered output rather than client re-fetching. |
| Nonce / referer failure | A stale page or a full-page cache served an expired nonce. Hard-refresh the page so a fresh tc_proxy_nonce is issued. |
Because data is rendered server-side into real HTML, tables are visible to search engines and to visitors even before any JavaScript runs. The proxy is only used for live re-fetching and interactive features.
Empty table or "no rows" messages
An empty table almost always means the data arrived but TableCrafter could not find a list of rows to render. The exact admin error tells you where it stopped:
| Error string | What it means |
|---|---|
| "Empty Source: The data received is empty." | The fetch succeeded but the body was empty. Verify the endpoint actually returns content. |
| "Empty Dataset: No rows found at this path." | The resolved data is an empty array. Your root path may point one level too deep, or the source genuinely has no rows right now. |
| "Structure Error: The target data is not a list/array." | The data at the chosen level is a single object/value, not a list. Adjust root to point at the array of records. |
| "Path Error: Key 'x' not found in data structure." | A segment in your root dot-path does not exist. See the next section. |
When your records are nested, use the root attribute with dot notation to point at the array. Given a response like { "data": { "items": [ ... ] } }:
[tablecrafter source="https://api.example.com/feed.json" root="data.items"]
A flat list of scalars (e.g. ["red","green","blue"]) is not "broken" — TableCrafter auto-wraps it into a single-column table with a Value header so it still renders.
"Path Error" — fixing the root attribute
The root path is split on dots and walked key by key. If any key is missing you get a precise Path Error naming the failing segment. To debug:
- Open the raw source URL in a browser and inspect the JSON shape.
- Trace the path from the top object down to the array of records.
- Remove a trailing segment if you over-shot into an individual row, or add one if your array is wrapped deeper.
Leave root empty when the top-level response is already an array of objects.
Columns are missing or in the wrong order
Headers are derived from the keys of the first row of your data. If a later row contains extra keys, those columns will not appear, because the header set is fixed from row one. Two attributes let you control columns explicitly:
| Attribute | Behavior |
|---|---|
| include Optional | Comma-separated allow-list of keys. Only these columns render, in the order you list them. Supports aliasing with key:Label. |
| exclude Optional | Comma-separated keys to hide. Applied after include. |
// Curate columns, rename two headers, and drop an internal field.
[tablecrafter source="..." include="name,price:Cost,symbol:Ticker" exclude="internal_id"]
If columns are still missing, check these likely causes:
- A key named in
includedoes not exist on the first row (it is silently skipped — names are case-sensitive and must match the source key exactly, not the displayed label). - The expected column only exists on later rows; reorder your data so a representative row is first, or add the key to every row at the source.
- Every requested header was excluded — when no headers remain, the table will not render at all.
Data is stale — cache & SWR behavior
TableCrafter uses a Stale-While-Revalidate (SWR) strategy to stay fast. Understanding the layers explains why an updated source may not appear instantly:
- Rendered HTML cache (transient prefix
tc_html_) and raw data cache (prefixtc_cache_) each live for up to 1 hour. - When a cached table is older than the 5-minute stale threshold, TableCrafter serves the cached copy instantly and schedules a background refresh (
tc_refresh_single_source) so the next view is fresh. - An hourly cron event (
tc_refresher_cron) warms tracked source URLs in the background.
The cache key incorporates the source plus rendering attributes (include, exclude, search, filters, export, per_page, sort) and the plugin version — so changing a shortcode attribute or updating the plugin naturally produces fresh output rather than a collision.
To force-clear everything immediately, use WP-CLI:
# Clear all TableCrafter transients (html, data, export, rate-limit).
wp tablecrafter clear-cache
# Re-fetch and warm the cache for all tracked source URLs.
wp tablecrafter warm-cache
For dashboards that must update on a fixed cadence, prefer the shortcode's auto_refresh and refresh_interval attributes (client-side polling through the proxy) over fighting the server cache. refresh_interval is in milliseconds and defaults to 300000 (5 minutes).
[tablecrafter source="..." auto_refresh="true" refresh_interval="60000" refresh_countdown="true"]
Elementor & Gutenberg editor quirks
TableCrafter ships a Gutenberg block, an Elementor widget, and the universal shortcode. Most editor-specific reports fall into the patterns below.
Empty placeholder instead of a table
Inside the block editor (or a REST preview), a configured-with-no-source block renders a friendly "Configure Your Data Source" placeholder rather than an error. This is expected — add a source URL in the sidebar and the live preview will populate.
Elementor widget not in the panel / fatal on activation
The widget is registered only on Elementor's own hooks and guarded by class-existence checks, so it loads safely whether or not Elementor is installed, and regardless of activation order. If the widget does not appear, confirm Elementor is active and clear any page-builder cache. The live preview deliberately caps rendering to a small slice (between 1 and 25 rows) for editor performance — the published page shows the full dataset.
Live Search / Export / Filters toggles "not working" in the editor
Toggling sidebar options changes the block's data attributes; the interactive table is hydrated from the server-rendered markup. If a toggle does not seem to apply, the editor is usually showing a cached preview — re-select the block or refresh the editor to force re-hydration.
The shortcode is the most portable integration. If a block or widget misbehaves in an unusual theme, drop in [tablecrafter source="..."] directly — it works on any theme back to classic setups and bypasses editor-specific rendering.
Turning on debug mode
For deeper diagnostics, enable the frontend debug logger. It prints proxy, fetch, and render details to the browser console:
// In the console, or set before TableCrafter's script runs.
window.TABLECRAFTER_DEBUG = true;
You can also scope debugging to a single table by adding data-debug="true" to its container. On the server side, set WP_DEBUG to true in wp-config.php so TableCrafter writes fetch failures, SSRF blocks, and rate-limit events to debug.log.
Quick reference checklist
- View the page as an admin to reveal the detailed Setup Guide error.
- Open the source URL in a browser — confirm it returns HTTP 200 and valid JSON/CSV.
- If "blocked for safety," the host is local/private — use a public URL.
- Empty table? Check the
rootpath and that row one carries every expected key. - Stale data? Run
wp tablecrafter clear-cacheor wait out the 5-minute stale window. - Editor oddities? Re-select the block/widget, clear builder cache, or fall back to the shortcode.
- Still stuck? Set
window.TABLECRAFTER_DEBUG = trueandWP_DEBUGand read the logs.
Next, see shortcode-reference.html for the full attribute list and caching-performance.html for a deeper look at the SWR cache and cron warming.