Files
2025-11-30 08:25:40 +08:00

16 KiB

TinaCMS Common Errors - Complete Reference

This document provides detailed troubleshooting for all 9 common TinaCMS errors.

Last Updated: 2025-10-24 TinaCMS Version: 2.9.0


Table of Contents

  1. ESbuild Compilation Errors
  2. Module Resolution Issues
  3. Field Naming Constraints
  4. Docker Binding Issues
  5. Missing _template Key Error
  6. Path Mismatch Issues
  7. Build Script Ordering Problems
  8. Failed Loading TinaCMS Assets
  9. Reference Field 503 Service Unavailable

1. ESbuild Compilation Errors

Error Messages

ERROR: Schema Not Successfully Built
ERROR: Config Not Successfully Executed
ERROR: Cannot bundle code that requires custom loaders

Causes

The tina/config.ts file is compiled using esbuild and executed in Node.js. This can fail when:

  1. Importing code with custom loaders

    • Webpack loaders
    • Babel plugins
    • PostCSS processors
    • SCSS/SASS files
    • Vue/Svelte components
  2. Importing frontend-only code

    • Code using window object
    • Code using DOM APIs
    • React hooks outside components
    • Browser-specific APIs
  3. Importing entire component libraries

    • import { Component } from '../components/' pulls in entire directory
    • May include dependencies not compatible with Node.js

Solutions

Solution 1: Import Specific Files Only

// ❌ Bad - Imports entire directory
import { HeroComponent } from '../components/'

// ✅ Good - Import specific file
import { HeroComponent } from '../components/blocks/hero'

// ❌ Bad - Imports entire library
import * as Components from '../components'

// ✅ Good - Import only what you need
import { HeroComponent, CTAComponent } from '../components/blocks'

Solution 2: Create Separate Schema Files

If your component definitions are complex, extract schema to separate files:

// components/hero/hero.schema.ts
export const heroBlockSchema = {
  name: 'hero',
  label: 'Hero Section',
  fields: [/* ... */]
}

// tina/config.ts
import { heroBlockSchema } from '../components/hero/hero.schema'

export default defineConfig({
  schema: {
    collections: [{
      fields: [{
        type: 'object',
        name: 'hero',
        ...heroBlockSchema
      }]
    }]
  }
})

Solution 3: Use Type Imports Only

// ✅ Import only types (doesn't execute code)
import type { HeroProps } from '../components/Hero'

Prevention

  • Keep tina/config.ts minimal
  • Only import type definitions and simple utilities
  • Avoid importing UI components
  • Extract schema definitions to separate files
  • Test config builds with npx @tinacms/cli@latest build

2. Module Resolution Issues

Error Messages

Error: Could not resolve "tinacms"
Module not found: Can't resolve 'tinacms'
Cannot find module 'tinacms' or its corresponding type declarations

Causes

  1. Corrupted Installation

    • Incomplete npm install
    • Network issues during installation
    • Corrupted node_modules
  2. Version Mismatches

    • Conflicting peer dependencies
    • React version mismatch
    • TinaCMS version incompatibility
  3. Missing Dependencies

    • react and react-dom not installed (required even for non-React frameworks)
    • @tinacms/cli not in devDependencies

Solutions

Solution 1: Clean Reinstall (npm)

# Remove node_modules and lock file
rm -rf node_modules package-lock.json

# Clear npm cache
npm cache clean --force

# Reinstall all dependencies
npm install

Solution 2: Clean Reinstall (pnpm)

# Remove node_modules and lock file
rm -rf node_modules pnpm-lock.yaml

# Clear pnpm cache
pnpm store prune

# Reinstall all dependencies
pnpm install

Solution 3: Clean Reinstall (yarn)

# Remove node_modules and lock file
rm -rf node_modules yarn.lock

# Clear yarn cache
yarn cache clean

# Reinstall all dependencies
yarn install

Solution 4: Ensure React Dependencies (Non-React Frameworks)

Even if you're using Hugo, Jekyll, or Eleventy, you MUST install React:

npm install react@^19 react-dom@^19

Prevention

  • Use lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock)
  • Don't use --no-optional or --omit=optional flags
  • Ensure react and react-dom are in dependencies
  • Keep TinaCMS versions consistent across packages

3. Field Naming Constraints

Error Messages

Field name contains invalid characters
Invalid field name: 'hero-image'
Field names must be alphanumeric

Causes

TinaCMS field names can ONLY contain:

  • Letters (a-z, A-Z)
  • Numbers (0-9)
  • Underscores (_)

NOT allowed:

  • Hyphens (-)
  • Spaces ( )
  • Special characters (!@#$%^&*)

This is a breaking change from Forestry.io which allowed hyphens.

Solutions

Solution 1: Use Underscores

// ❌ Bad
{
  name: 'hero-image',
  label: 'Hero Image',
  type: 'image'
}

// ✅ Good
{
  name: 'hero_image',
  label: 'Hero Image',
  type: 'image'
}

Solution 2: Use camelCase

// ✅ Also good
{
  name: 'heroImage',
  label: 'Hero Image',
  type: 'image'
}

Solution 3: Migrate Existing Content

If migrating from Forestry.io with hyphenated field names:

# Find all files with hyphenated frontmatter
find content -name "*.md" -exec sed -i 's/hero-image:/hero_image:/g' {} +
find content -name "*.md" -exec sed -i 's/cover-photo:/cover_photo:/g' {} +

Prevention

  • Use underscores or camelCase for all field names
  • Document naming convention in team style guide
  • Use linter/validator to catch invalid names

4. Docker Binding Issues

Error Messages

Connection refused: http://localhost:3000
Cannot connect to TinaCMS admin interface
ERR_CONNECTION_REFUSED

Causes

By default, development servers bind to 127.0.0.1 (localhost only), which prevents access from:

  • Docker containers
  • Network devices
  • Remote connections

Solutions

Solution 1: Next.js

tinacms dev -c "next dev --hostname 0.0.0.0"

Or in package.json:

{
  "scripts": {
    "dev": "tinacms dev -c \"next dev --hostname 0.0.0.0\""
  }
}

Solution 2: Vite

tinacms dev -c "vite --host 0.0.0.0"

Or in vite.config.ts:

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 3000
  }
})

Solution 3: Astro

tinacms dev -c "astro dev --host 0.0.0.0"

Or in astro.config.mjs:

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 4321
  }
})

Solution 4: Docker Compose

version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    command: npm run dev  # Must include --hostname 0.0.0.0

Prevention

  • Always use --host 0.0.0.0 in Docker environments
  • Configure framework config files instead of CLI flags
  • Document binding requirements in README

5. Missing _template Key Error

Error Messages

GetCollection failed: Unable to fetch
template name was not provided
Error: Document missing _template field

Causes

When a collection uses templates array (multiple schemas), each document must include a _template field in its frontmatter specifying which template to use.

Solutions

If you only have one schema, use fields instead of templates:

// ❌ Using templates (requires _template in frontmatter)
{
  name: 'post',
  path: 'content/posts',
  templates: [
    {
      name: 'article',
      fields: [/* ... */]
    }
  ]
}

// ✅ Using fields (no _template needed)
{
  name: 'post',
  path: 'content/posts',
  fields: [/* ... */]  // Same fields as above
}

Solution 2: Add _template to Frontmatter

If you need multiple templates, ensure all documents have _template:

---
_template: article
title: My Blog Post
date: 2025-10-24
---

Content here...

Solution 3: Batch Update Existing Files

# Add _template field to all files
find content/posts -name "*.md" -exec sed -i '1a _template: article' {} +

Solution 4: Set Default Template

{
  name: 'post',
  path: 'content/posts',
  templates: [
    {
      name: 'article',
      fields: [/* ... */]
    }
  ],
  ui: {
    defaultItem: () => ({
      _template: 'article'  // Default template for new documents
    })
  }
}

Prevention

  • Use fields for single-schema collections
  • Use templates only when you truly need multiple schemas
  • Document _template requirement in team guidelines

6. Path Mismatch Issues

Error Messages

No files found in collection
File not found: content/posts/hello.md
GraphQL query returned empty results

Causes

The path in your collection config doesn't match where files actually live.

Solutions

Solution 1: Verify File Locations

# Check where your files actually are
ls -R content/

# If files are in content/posts/
# Your config should be:
{
  name: 'post',
  path: 'content/posts'  // ✅ Matches file location
}

Solution 2: No Trailing Slash

// ❌ Bad
{
  path: 'content/posts/'  // Trailing slash may cause issues
}

// ✅ Good
{
  path: 'content/posts'   // No trailing slash
}

Solution 3: Use Relative Paths

// ✅ Path relative to project root
{
  path: 'content/posts'
}

// ❌ Don't use absolute paths
{
  path: '/home/user/project/content/posts'
}

Solution 4: Audit Paths

# TinaCMS CLI includes audit command
npx @tinacms/cli@latest audit

# Shows which files match collections
# Helps identify path mismatches

Prevention

  • Always verify file structure before setting path
  • Use npx @tinacms/cli@latest audit to check paths
  • Document expected file structure in README

7. Build Script Ordering Problems

Error Messages

ERROR: Cannot find module '../tina/__generated__/client'
ERROR: Property 'queries' does not exist on type '{}'
Type error: Module '../tina/__generated__/client' has no exported member 'client'

Causes

Framework build runs before tinacms build, so generated TypeScript types don't exist yet.

Solutions

Solution 1: Correct Script Order

{
  "scripts": {
    "build": "tinacms build && next build"  // ✅ Tina FIRST
    // NOT: "build": "next build && tinacms build"  // ❌ Wrong
  }
}

Solution 2: CI/CD Fix (GitHub Actions)

name: Build

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install dependencies
        run: npm install

      - name: Build Tina
        run: npx @tinacms/cli@latest build  # Generate types FIRST

      - name: Build App
        run: npm run build  # Now framework build works

Solution 3: Vercel Configuration

// vercel.json
{
  "buildCommand": "tinacms build && next build"
}

Solution 4: Netlify Configuration

# netlify.toml
[build]
  command = "tinacms build && npm run build"

Prevention

  • Always run tinacms build before framework build
  • Document correct build order in README
  • Add build order to CI/CD templates

8. Failed Loading TinaCMS Assets

Error Messages

Failed to load resource: net::ERR_CONNECTION_REFUSED
http://localhost:4001/...
Mixed Content: The page was loaded over HTTPS, but requested an insecure resource

Causes

  1. Development admin/index.html pushed to production

    • Dev version loads assets from localhost:4001
    • Production needs built assets
  2. Subdirectory deployment without basePath config

    • Site deployed to example.com/blog/
    • TinaCMS tries to load from example.com/admin/ (fails)

Solutions

Solution 1: Always Build for Production

{
  "scripts": {
    "build": "tinacms build && next build"  // ✅ Always build
    // NOT: "build": "tinacms dev"           // ❌ Never dev in prod
  }
}

Solution 2: Configure basePath for Subdirectories

If site is at example.com/blog/:

// tina/config.ts
export default defineConfig({
  build: {
    outputFolder: 'admin',
    publicFolder: 'public',
    basePath: 'blog'  // ← Subdirectory path
  }
})

Solution 3: Verify .gitignore

# .gitignore

# TinaCMS
.tina/__generated__
admin/index.html  # ← Don't commit dev admin

# But DO commit:
# public/admin/  (production build)

Solution 4: CI/CD Always Build

# GitHub Actions
- run: npx @tinacms/cli@latest build  # Not dev!

Prevention

  • Use tinacms build in all CI/CD
  • Set basePath for subdirectory deploys
  • Don't commit admin/index.html from dev mode

9. Reference Field 503 Service Unavailable

Error Messages

503 Service Unavailable
Request timed out
Reference field dropdown not loading

Causes

Reference fields load ALL documents from the referenced collection. If the collection has 100s or 1000s of items, the request times out.

Current Limitation: TinaCMS does not support pagination for reference fields (as of v2.9.0).

Solutions

Solution 1: Split Large Collections

Instead of one huge collection, split by category:

// ❌ Single huge collection (1000+ authors)
{
  name: 'author',
  path: 'content/authors',
  fields: [/* ... */]
}

// ✅ Split into manageable sizes
{
  name: 'active_author',
  label: 'Active Authors',
  path: 'content/authors/active',
  fields: [/* ... */]
}

{
  name: 'archived_author',
  label: 'Archived Authors',
  path: 'content/authors/archived',
  fields: [/* ... */]
}

Solution 2: Use String Field with Validation

Instead of reference, use a string select:

// ❌ Reference field (times out with 100+ authors)
{
  type: 'reference',
  name: 'author',
  collections: ['author']
}

// ✅ String field with curated options
{
  type: 'string',
  name: 'author_id',
  label: 'Author',
  options: [
    { label: 'John Doe', value: 'john-doe' },
    { label: 'Jane Smith', value: 'jane-smith' },
    // ... curated list of ~50 authors max
  ]
}

Solution 3: Custom Field Component (Advanced)

Create a custom field component with client-side pagination:

// Advanced: Requires custom React component
{
  type: 'string',
  name: 'author_id',
  ui: {
    component: 'author-select-paginated'  // Your custom component
  }
}

See: https://tina.io/docs/extending-tina/custom-field-components/

Solution 4: Use External Service

For very large datasets, consider:

  • Store authors in D1/KV
  • Use custom field component to query via API
  • Load data on-demand with search/filter

Prevention

  • Limit referenced collections to <100 items
  • Use string fields for large datasets
  • Consider alternative architectures for huge collections

Additional Resources

Official Documentation

Support


Last Updated: 2025-10-24 Errors Documented: 9 Prevention Rate: 100%