clockworklabs/SpacetimeDB · error · Unauthorized
{} is not authorized to perform action{}: {}
Error message
{} is not authorized to perform action{}: {} What it means
The client API's authorization failure: the authenticated identity (subject) was denied permission for the requested Action on the given database. It maps to HTTP 403 Forbidden with this rendered message; the code documents that 401 is used only for missing or invalid credentials, so this 403 means 'valid identity, insufficient permission'.
Source
Thrown at crates/client-api/src/lib.rs:549
database.map(|ident| format!(" on database {ident}")).unwrap_or_default(),
action
)]
Unauthorized {
subject: Identity,
action: Action,
// `Option` for future, non-database-bound actions.
database: Option<Identity>,
#[source]
source: Option<anyhow::Error>,
},
#[error("authorization failed due to internal error")]
InternalError(#[from] anyhow::Error),
}
impl axum::response::IntoResponse for Unauthorized {
fn into_response(self) -> axum::response::Response {
let (status, e) = match self {
unauthorized @ Self::Unauthorized { .. } => (StatusCode::FORBIDDEN, anyhow!(unauthorized)),
Self::InternalError(e) => {
log::error!("internal error: {e:#}");
(StatusCode::INTERNAL_SERVER_ERROR, e)
}
};
(status, format!("{e:#}")).into_response()
}
}
/// Action to be authorized via [Authorization::authorize_action].
#[derive(Clone, Copy, Debug)]
pub enum Action {
CreateDatabase {
parent: Option<Identity>,
organization: Option<Identity>,
},
UpdateDatabase,View on GitHub (pinned to 524b4487d9)
Solutions
- Check who you are on that server: `spacetime identity show` / `spacetime server whoami` against the right --server.
- Switch to the owning identity: `spacetime identity switch <identity-or-email>` or log in again.
- Verify the database name/address exists on the target server: `spacetime list`.
- If you own the database and still see this, confirm your server and identity correspond to the ones that published it.
- Otherwise ask the owner to grant access, or publish under a new name.
Defensive patterns
Strategy: try-catch
Validate before calling
const dbs = await fetch(`${server}/v1/databases`, { headers: { Authorization: `Bearer ${token}` } });
if (dbs.status === 403) {
throw new Error('identity lacks rights on this server — run `spacetime identity show` and switch owner identity');
} Try / catch
try {
await spacetimePublish(dbName, modulePath);
} catch (e) {
if (String(e).includes('is not authorized to perform action')) {
// 403: valid identity, wrong permissions — do NOT retry blindly
await spacetrySwitchIdentity(dbOwnerHint);
throw new Error(`not owner of '${dbName}' — switch identity or ask owner for access`);
}
throw e;
} Prevention
- Run `spacetime identity show` / `spacetime server whoami` before publish.
- Keep one identity per project and store it in the repo's spacetime.json.
- Treat 403 as a permission problem and 401 as a credential problem; only re-login fixes 401.
When it happens
Trigger: Publishing or updating a database owned by a different identity; calling a reducer or reading logs on a database the current identity does not own and has not been granted access to; using a token minted on server A against server B where the identity owns nothing; specifying an owner identity that does not match the logged-in identity during publish.
Common situations: Multiple identities in the keychain with the wrong one active; switching between local and testnet servers without re-login; team members sharing a database address without the owner granting publish rights; stale SPACETIMEDB_SPACETIME_TOKEN in CI.
Related errors
- Issuer too long: {:?}
- Subject too long: {:?}
- Issuer empty
- Subject empty
- Identity mismatch: token identity {token_identity:?} does no
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/e9c0f78cd9d14604.
Report an issue: GitHub.