ByJoel GoldfootUpdated
Troubleshooting
Common problems and solutions organized by Defense in Depth layer. Start with the quick diagnostics script to identify which layers need attention.
Quick Diagnostics
Run this bash script to check all five layers against any URL. It takes under 10 seconds and identifies which layers need work.
#!/bin/bash
# quick-bimodal-check.sh — paste your URL and run
# Usage: bash quick-bimodal-check.sh https://yoursite.com
URL="${1:-https://yoursite.com}"
PASS=0
FAIL=0
check() {
local desc="$1"
local cmd="$2"
local expect="$3"
local result
result=$(eval "$cmd" 2>/dev/null)
if echo "$result" | grep -q "$expect"; then
echo " PASS $desc"
((PASS++))
else
echo " FAIL $desc"
((FAIL++))
fi
}
echo "=== BiModal Quick Diagnostics: $URL ==="
echo ""
echo "Layer 1: FR-1 Compliance"
check "Content in initial response" \
"curl -s '$URL' | grep -c 'data-agent'" \
"[1-9]"
check "Page title present" \
"curl -s '$URL' | grep -c '<title>'" \
"[1-9]"
check "Not pure SPA (no #/root-only markup)" \
"curl -s '$URL' | grep -v 'id="root">' | grep -c '<main'" \
"[1-9]"
echo ""
echo "Layer 2: Semantic Structure"
check "HTML landmarks present" \
"curl -s '$URL' | grep -c '<main\|<nav\|<header\|<footer'" \
"[1-9]"
check "data-agent-page attribute" \
"curl -s '$URL' | grep -c 'data-agent-page'" \
"[1-9]"
check "Skip-to-content link" \
"curl -s '$URL' | grep -c 'skip-to-content\|skip-to-main'" \
"[1-9]"
echo ""
echo "Layer 3: Structured Data"
check "JSON-LD present" \
"curl -s '$URL' | grep -c 'application/ld+json'" \
"[1-9]"
check "Schema.org reference" \
"curl -s '$URL' | grep -c 'schema.org'" \
"[1-9]"
echo ""
echo "Layer 4: API Discovery"
check "API link tag present" \
"curl -s '$URL' | grep -c 'rel=.api.'" \
"[1-9]"
check "OpenAPI spec accessible" \
"curl -s -o /dev/null -w '%{http_code}' '$URL/api/openapi.json'" \
"200"
echo ""
echo "Layer 5: Agent Protocols"
check "robots.txt accessible" \
"curl -s -o /dev/null -w '%{http_code}' '$URL/robots.txt'" \
"200"
check "Agent Card accessible" \
"curl -s -o /dev/null -w '%{http_code}' '$URL/.well-known/agent.json'" \
"200"
echo ""
echo "Results: $PASS passed, $FAIL failed"
[ $FAIL -eq 0 ] && echo "All checks passed!" || echo "Fix the FAIL items above."Save as quick-bimodal-check.sh and run: bash quick-bimodal-check.sh https://yoursite.com
Layer 1: FR-1 Problems
Content Accessibility — most common failure point
curl returns empty content
curl -s https://yoursite.com | grep -c "data-agent" returns 0
Diagnosis
Content is being rendered client-side only. The initial HTTP response contains only a JS bundle reference and an empty div.
Solution
Move data fetching from useEffect/client components to server components or getServerSideProps. Check the Network tab in DevTools — if the document response contains <div id="root"></div> with no child content, you have an FR-1 violation.
// BEFORE (FR-1 violation — client-rendered)
'use client';
export default function Products() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts);
}, []);
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
// AFTER (FR-1 compliant — server-rendered)
export default async function Products() {
const products = await fetch('/api/products').then(r => r.json());
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}Content present in browser but not in curl
Site looks correct visually but curl output shows no product data
Diagnosis
Hydration is working but the initial HTML is empty. Common with SSR frameworks that are misconfigured or have data-fetching in client components.
Solution
In Next.js App Router, ensure data fetching happens in Server Components (files without "use client"). In Pages Router, use getServerSideProps or getStaticProps, not useEffect.
// Check: does your page component fetch on client or server?
// In Next.js App Router, any async function = server component
export default async function Page({ params }) {
// This runs on the server — data is in initial HTML
const data = await getData(params.id);
return <div data-agent-page="product">{data.name}</div>;
}Layer 2: Semantic Problems
Semantic Structure — heading hierarchy and attribute coverage
No data-agent-* attributes in HTML output
curl | grep "data-agent" returns 0 even though site renders correctly
Diagnosis
Attributes were added to JSX but the component is client-rendered, or they were added to a component that gets stripped during build.
Solution
Verify attributes appear in the server-rendered HTML. Check: curl -s https://yoursite.com | grep "data-agent-page". If missing, trace which component renders <main> and ensure it is a server component.
Multiple h1 elements on page
Accessibility audit or validator reports multiple h1s
Diagnosis
Layout components or shared headers may be adding their own h1, or multiple sections each have an h1.
Solution
Keep exactly one h1 per page. Use h2 for section titles. The h1 should match the page <title> tag content and have data-agent-content="page-title".
<!-- WRONG: two h1s -->
<h1>Site Name</h1> <!-- in header -->
<h1>Product Name</h1> <!-- in content -->
<!-- RIGHT: one h1 -->
<header>
<a href="/">Site Name</a> <!-- not a heading -->
</header>
<main>
<h1 data-agent-content="page-title">Product Name</h1>
</main>data-agent-* attributes missing on dynamic content
Static pages have attributes, dynamically loaded content does not
Diagnosis
Attributes are added correctly in JSX but the dynamic section is rendered client-side without them.
Solution
If a component must be client-rendered (e.g., for interactivity), ensure the initial server render includes a fallback with proper attributes, or use Suspense with a server-rendered skeleton.
// Suspense with server-rendered fallback
import { Suspense } from 'react';
export default function Page() {
return (
<main data-agent-page="product-listing" data-agent-intent="browse">
<Suspense
fallback={
<div
data-agent-component="product-list"
data-agent-mitigation="skeleton"
>
Loading products...
</div>
}
>
<ProductList />
</Suspense>
</main>
);
}Layer 3: Structured Data Problems
Schema.org and JSON-LD validation
JSON-LD not in initial HTML
Google Rich Results Test shows no structured data; curl | grep "ld+json" returns 0
Diagnosis
JSON-LD is being injected by JavaScript after page load, or added via a client component.
Solution
In Next.js, add JSON-LD in the page server component or in the <head> via generateMetadata. Never inject via useEffect.
// Next.js App Router — JSON-LD in server component
export default async function ProductPage({ params }) {
const product = await getProduct(params.id);
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'USD',
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<main data-agent-page="product-detail">
{/* ... */}
</main>
</>
);
}Rich Results Test shows errors
Schema.org validation fails with "missing required property" errors
Diagnosis
Required properties for the Schema.org @type are missing. Each type has specific required and recommended fields.
Solution
Check the Schema.org type documentation for required properties. Common: Product needs name, offers; Article needs headline, author, datePublished; Organization needs name, url.
Layer 4: API Problems
OpenAPI spec and CORS configuration
OpenAPI spec returns 404
curl https://yoursite.com/api/openapi.json returns 404
Diagnosis
The OpenAPI file has not been deployed, or the route is not configured.
Solution
In Next.js, create app/api/openapi.json/route.ts to serve the spec. Or place openapi.json in the /public directory to serve it as a static file at /openapi.json.
// app/api/openapi.json/route.ts
import { NextResponse } from 'next/server';
import spec from '@/lib/openapi-spec.json';
export async function GET() {
return NextResponse.json(spec, {
headers: {
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'public, max-age=3600',
},
});
}Agents get CORS errors when calling API
API calls from agent contexts fail with "CORS policy" errors
Diagnosis
API routes are missing CORS headers. Agents making cross-origin requests need CORS headers on all API responses.
Solution
Add Access-Control-Allow-Origin and related headers to all API responses. In Next.js, configure CORS in next.config.js headers or in individual route handlers.
// app/api/products/route.ts
export async function GET(req: Request) {
const products = await getProducts();
return new Response(JSON.stringify(products), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
export async function OPTIONS() {
return new Response(null, {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}Layer 5: Protocol Problems
MCP server and Agent Card issues
Agent Card returns 404
curl https://yoursite.com/.well-known/agent.json returns 404
Diagnosis
The .well-known directory is not served, or the file does not exist.
Solution
In Next.js, place agent.json in public/.well-known/agent.json — Next.js serves public files at their path. Alternatively, create a route handler at app/.well-known/agent.json/route.ts.
// Option 1: static file
// Place at: public/.well-known/agent.json
// Served at: /.well-known/agent.json
// Option 2: route handler
// app/.well-known/agent.json/route.ts
export async function GET() {
return Response.json({
schemaVersion: '1.0',
name: 'YourSite Agent',
// ...
});
}MCP server not discovered by agents
Agents can not connect to MCP server
Diagnosis
MCP servers are not auto-discovered from web pages yet — they need to be explicitly configured in each agent client. The Agent Card documents the MCP server URL for manual configuration.
Solution
Add the MCP server URL to your Agent Card under a custom extension field. Document the connection endpoint clearly in your API docs and Agent Card description.
Framework-Specific Issues
Pure client-side rendering — all content is CSR by default
Migrate to Next.js (App Router) or add SSR via React Server Components. As a temporary mitigation, add a static HTML fallback served by your CDN.
getServerSideProps / getStaticProps not used — defaulting to CSR
Add getStaticProps for static content, getServerSideProps for dynamic content. Avoid useEffect data fetching for primary content.
'use client' on pages or layout components that fetch data
Remove 'use client' from data-fetching components. Move data fetching to the server component parent. Only add 'use client' for interactive UI leaves.
useFetch or useAsyncData called only on client
In Nuxt 3, useFetch runs on both server and client by default. Ensure you are not using client-only composables. Check with: nuxt build && curl localhost:3000.
Client:only directive used for content components
client:only bypasses SSR entirely. Use client:load or client:visible for interactive components, not for content. Move content to Astro component props (server-rendered).
SEO & GEO Issues
GEO (Generative Engine Optimization) issues — content not appearing in AI-generated answers.
Page not appearing in AI overviews (Google SGE / Bing Copilot)
Cause: Missing structured data, or content not accessible to Googlebot-Extended
Add JSON-LD for the page entity type. Verify Googlebot is not blocked in robots.txt. Ensure canonical URL is set correctly.
AI assistants give outdated information about your site
Cause: Crawlers are seeing stale content, or cached CSR content
Update Cache-Control headers to expire appropriately. Submit updated sitemap to Google Search Console. Consider lower TTL on CDN for agent-critical pages.
Citation rate in AI responses is low
Cause: Content structure makes it hard for AI to extract and attribute your content
Add clear authorship (author Schema.org property), publication dates, and source URLs to all content. Use Article or NewsArticle schema with publisher Organization.
robots.txt blocking AI crawlers
Cause: Blanket Disallow: / or user-agent rules that match AI crawlers
Add explicit Allow rules for known AI crawlers. Common agents: GPTBot (OpenAI), ClaudeBot (Anthropic), Google-Extended (Google AI), PerplexityBot, YouBot.
# robots.txt — allow AI crawlers
User-agent: GPTBot
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: PerplexityBot
Allow: /
# Block all bots from admin
User-agent: *
Disallow: /admin/
Disallow: /api/admin/