risingwavelabs/risingwave · error · SinkError::SqlServer

SQL Server user {} lacks required write permission(s) {} on

Error message

SQL Server user {} lacks required write permission(s) {} on table {}

What it means

After evaluating the permission query, RisingWave compares the grants it found (can_select/insert/update/delete as required by append-only vs upsert mode) against what is needed. If any required permission is missing it throws this error listing the user, the missing permission names joined by commas, and the target table. It is a deliberate preflight check so sinks fail fast instead of erroring mid-stream on write.

Source

Thrown at src/connector/src/sink/sqlserver.rs:699

    };
    let ColumnData::I32(can_update) = iter.next().ok_or_else(permission_query_error)? else {
        return Err(permission_query_error());
    };
    let ColumnData::I32(can_delete) = iter.next().ok_or_else(permission_query_error)? else {
        return Err(permission_query_error());
    };

    let missing_permissions = missing_sql_server_write_permissions(
        is_append_only,
        permission_is_granted(can_insert),
        permission_is_granted(can_update),
        permission_is_granted(can_delete),
    );
    if missing_permissions.is_empty() {
        return Ok(());
    }

    Err(SinkError::SqlServer(anyhow!(format!(
        "SQL Server user {} lacks required write permission(s) {} on table {}",
        config.user,
        missing_permissions.join(", "),
        config.full_object_path()
    ))))
}

fn permission_is_granted(permission_value: Option<i32>) -> bool {
    permission_value == Some(1)
}

fn missing_sql_server_write_permissions(
    is_append_only: bool,
    can_insert: bool,
    can_update: bool,
    can_delete: bool,
) -> Vec<&'static str> {
    let mut missing_permissions = vec![];

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Grant the missing permissions in SQL Server: e.g. `GRANT INSERT, UPDATE, DELETE ON <schema>.<table> TO <user>;` (match what the error lists).
  2. Ensure the user in the sink config is the same principal you granted permissions to (check for login vs user mismatch).
  3. Check for explicit DENY entries overriding grants: `SELECT * FROM sys.database_permissions WHERE grantee_principal_id = USER_ID('<user>')`.
  4. If the sink is append-only, INSERT alone suffices; alternatively switch sink mode to match the permissions actually granted.

Example fix

-- before: user has only SELECT
GRANT SELECT ON dbo.orders TO rw_sink;
-- after: grant writes required by an upsert sink
GRANT SELECT, INSERT, UPDATE, DELETE ON dbo.orders TO rw_sink;
Defensive patterns

Strategy: validation

Validate before calling

-- preflight: list granted permissions for the sink user
SELECT permission_name FROM fn_my_permissions('dbo.orders', 'OBJECT')
WHERE permission_name IN ('SELECT','INSERT','UPDATE','DELETE');

Try / catch

match sink.validate().await {
    Err(e) if e.to_string().contains("lacks required write permission") => {
        request_grants_from_dba(&parse_missing_permissions(&e.to_string()));
    }
    other => other,
}

Prevention

When it happens

Trigger: `validate` -> `validate_sql_server_write_permission`: the query result shows the configured `config.user` lacks one or more of INSERT/UPDATE/DELETE (and SELECT for upsert) on the table. E.g. an append-only sink without INSERT, or an upsert sink missing UPDATE/DELETE.

Common situations: DBA granted permissions to a different login than the one in the sink config; role membership not applied; table owner changed; permissions granted only at schema level with DENY overrides; user created without login mapping.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/9ea83bed166be88a. Report an issue: GitHub.