vitessio/vitess · error · ErrUnauthorized
%w: cannot create schema migration in %s
Error message
%w: cannot create schema migration in %s
What it means
VTAdmin's ApplySchema RPC returns this when the authenticated caller lacks the RBAC 'create' action on the SchemaMigration resource for the requested cluster. The API checks authorization before touching the cluster and fails fast with a wrapped errors.ErrUnauthorized. It is a policy decision, not an infrastructure failure.
Source
Thrown at go/vt/vtadmin/api.go:486
// Maintain order of clusters when removing dynamic cluster
clusterIndex := stdsort.Search(len(api.clusters), func(i int) bool { return api.clusters[i].ID == key })
if clusterIndex >= len(api.clusters) || clusterIndex < 0 {
log.Error(fmt.Sprintf("Cannot remove cluster %s from api.clusters. Cluster index %d is out of range for clusters slice of %d length.", key, clusterIndex, len(api.clusters)))
}
api.clusters = append(api.clusters[:clusterIndex], api.clusters[clusterIndex+1:]...)
}
// ApplySchema is part of the vtadminpb.VTAdminServer interface.
func (api *API) ApplySchema(ctx context.Context, req *vtadminpb.ApplySchemaRequest) (*vtctldatapb.ApplySchemaResponse, error) {
span, ctx := trace.NewSpan(ctx, "API.ApplySchema")
defer span.Finish()
span.Annotate("cluster_id", req.ClusterId)
if !api.authz.IsAuthorized(ctx, req.ClusterId, rbac.SchemaMigrationResource, rbac.CreateAction) {
return nil, fmt.Errorf("%w: cannot create schema migration in %s", errors.ErrUnauthorized, req.ClusterId)
}
c, err := api.getClusterForRequest(req.ClusterId)
if err != nil {
return nil, err
}
// Parser with default options. New() itself initializes with default MySQL version.
parser, err := sqlparser.New(sqlparser.Options{
TruncateUILen: 512,
TruncateErrLen: 0,
})
if err != nil {
return nil, err
}
// Split the sql statement received from request.
sqlParts, err := parser.SplitStatementToPieces(req.Sql)View on GitHub (pinned to 01a25a7d17)
Solutions
- Update the caller's role in the vtadmin RBAC config to include action 'create' on the schema-migration resource for the target cluster
- Verify the cluster ID in the request matches the cluster the role is scoped to (use wildcards like '*' if intended)
- Restart vtadmin after editing the RBAC config and re-authenticate so new permissions take effect
Example fix
// before (rbac.yaml role)
rules:
- resource: schema-migration
actions: [get]
// after
rules:
- resource: schema-migration
actions: [get, create] Defensive patterns
Strategy: validation
Validate before calling
const canApply = await fetch('/api/permissions').then(r => r.json()).then(p => p.some(rule => rule.resource === 'schema-migration' && (rule.actions.includes('create') || rule.actions.includes('*')) && (rule.clusters.includes(clusterId) || rule.clusters.includes('*'))));
if (!canApply) throw new Error('not authorized to create schema migration in ' + clusterId); Type guard
function isUnauthorized(err: unknown): err is Error {
return err instanceof Error && err.message.includes('cannot create schema migration');
} Try / catch
try {
await applySchema(clusterId, sql);
} catch (err) {
if (String(err).includes('cannot create schema migration')) {
notifyAdminForRbacGrant(clusterId);
} else {
throw err;
}
} Prevention
- Check the vtadmin RBAC config for your role before automating schema operations
- Scope roles per cluster and verify the cluster ID you pass matches
- Re-authenticate after any RBAC change
When it happens
Trigger: Calling POST /schema/apply (ApplySchema) with a ClusterId whose RBAC role for the caller does not include create permission on schema migrations.
Common situations: Users whose RBAC config grants read-only or schema-read roles attempting to apply DDL via vtadmin; misconfigured rbac config YAML missing the schema-migration resource/action mapping; passing the wrong cluster ID so the matched role denies the action.
Related errors
- %w: cannot cancel schema migration in %s
- %w: cannot cleanup schema migration in %s
- %w: cannot complete schema migration in %s
- %w: cannot launch schema migration in %s
- %w: cannot retry schema migration in %s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/04f36a1a522c3143.
Report an issue: GitHub.