juicedata/juicefs · error

create table node, symlink, xattr: %s

Error message

create table node, symlink, xattr: %s

What it means

syncAllTables (pkg/meta/sql.go:585) creates/auto-migrates the core inode tables `node`, `symlink`, and `xattr` via xorm Sync2; failure is wrapped as "create table node, symlink, xattr: %s". These are the central tables of the metadata schema, so any DDL failure here prevents the volume from initializing. Duplicate-key races are ignored by syncTable; everything else propagates.

Source

Thrown at pkg/meta/sql.go:586

}

func (m *dbMeta) syncTable(beans ...interface{}) error {
	err := m.db.Sync2(beans...)
	if err != nil && strings.Contains(err.Error(), "Duplicate key") {
		err = nil
	}
	return err
}

func (m *dbMeta) syncAllTables() error {
	if err := m.syncTable(new(setting), new(counter)); err != nil {
		return fmt.Errorf("create table setting, counter: %s", err)
	}
	if err := m.syncTable(new(edge)); err != nil {
		return fmt.Errorf("create table edge: %s", err)
	}
	if err := m.syncTable(new(node), new(symlink), new(xattr)); err != nil {
		return fmt.Errorf("create table node, symlink, xattr: %s", err)
	}
	if err := m.syncTable(new(chunk), new(sliceRef), new(delslices)); err != nil {
		return fmt.Errorf("create table chunk, chunk_ref, delslices: %s", err)
	}
	if err := m.syncTable(new(session2), new(sustained), new(delfile)); err != nil {
		return fmt.Errorf("create table session2, sustaind, delfile: %s", err)
	}
	if err := m.syncTable(new(flock), new(plock), new(dirQuota), new(userGroupQuota)); err != nil {
		return fmt.Errorf("create table flock, plock, dirQuota, userGroupQuota: %s", err)
	}
	if err := m.syncTable(new(dirStats)); err != nil {
		return fmt.Errorf("create table dirStats: %s", err)
	}
	if err := m.syncTable(new(detachedNode)); err != nil {
		return fmt.Errorf("create table detachedNode: %s", err)
	}
	if err := m.syncTable(new(acl)); err != nil {
		return fmt.Errorf("create table acl: %s", err)

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Inspect the wrapped driver error for 'denied', 'read-only', or 'disk full' and fix that root cause.
  2. Ensure the DB user has CREATE and ALTER privileges so Sync2 can add missing columns to existing tables.
  3. After backup, drop incompatible leftover tables from an aborted init and retry.
  4. Free disk space on the database host if the error indicates storage exhaustion.
  5. Re-run the command; table creation is idempotent once the underlying issue is fixed.
Defensive patterns

Strategy: validation

Validate before calling

// Preflight: check core tables are absent or migrateable and user has ALTER
// MySQL:
//   SELECT COUNT(*) FROM information_schema.columns
//     WHERE table_schema='jfs' AND table_name='jfs_node';
//   SHOW GRANTS FOR CURRENT_USER();  -- must include ALTER/CREATE
// If jfs_node exists with an old schema, plan a backup before upgrading.

Try / catch

if err := initMeta(); err != nil {
    if strings.Contains(err.Error(), "create table node, symlink, xattr") {
        return fmt.Errorf("core inode table migration failed; check ALTER grants and schema version: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Metadata engine initialization (`juicefs format`/`mount`) when Sync2 on the node/symlink/xattr beans fails: missing CREATE privileges, existing incompatible tables from an older JuiceFS version, or DB resource limits (disk, max tables).

Common situations: Upgrading a volume where an old node table lacks newer columns and ALTER is denied; a DB user downgraded to read-only after initial format; SQLite file on a full disk.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/4fca18b4176c2c82. Report an issue: GitHub.