Skip to main content

ByUpdated

Implementation

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

React (CRA / Vite)

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.

Next.js (Pages Router)

getServerSideProps / getStaticProps not used — defaulting to CSR

Add getStaticProps for static content, getServerSideProps for dynamic content. Avoid useEffect data fetching for primary content.

Next.js (App Router)

'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.

Vue / Nuxt

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.

Astro

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/