{"record":{"id":"e9c0f78cd9d14604","repo":"clockworklabs/SpacetimeDB","slug":"is-not-authorized-to-perform-action","errorCode":null,"errorMessage":"{} is not authorized to perform action{}: {}","messagePattern":"(.+?) is not authorized to perform action(.+?): (.+?)","errorType":"http","errorClass":"Unauthorized","httpStatus":403,"severity":"error","filePath":"crates/client-api/src/lib.rs","lineNumber":549,"sourceCode":"        database.map(|ident| format!(\" on database {ident}\")).unwrap_or_default(),\n        action\n    )]\n    Unauthorized {\n        subject: Identity,\n        action: Action,\n        // `Option` for future, non-database-bound actions.\n        database: Option<Identity>,\n        #[source]\n        source: Option<anyhow::Error>,\n    },\n    #[error(\"authorization failed due to internal error\")]\n    InternalError(#[from] anyhow::Error),\n}\n\nimpl axum::response::IntoResponse for Unauthorized {\n    fn into_response(self) -> axum::response::Response {\n        let (status, e) = match self {\n            unauthorized @ Self::Unauthorized { .. } => (StatusCode::FORBIDDEN, anyhow!(unauthorized)),\n            Self::InternalError(e) => {\n                log::error!(\"internal error: {e:#}\");\n                (StatusCode::INTERNAL_SERVER_ERROR, e)\n            }\n        };\n\n        (status, format!(\"{e:#}\")).into_response()\n    }\n}\n\n/// Action to be authorized via [Authorization::authorize_action].\n#[derive(Clone, Copy, Debug)]\npub enum Action {\n    CreateDatabase {\n        parent: Option<Identity>,\n        organization: Option<Identity>,\n    },\n    UpdateDatabase,","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/client-api/src/lib.rs#L531-L567","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const dbs = await fetch(`${server}/v1/databases`, { headers: { Authorization: `Bearer ${token}` } });\nif (dbs.status === 403) {\n  throw new Error('identity lacks rights on this server — run `spacetime identity show` and switch owner identity');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await spacetimePublish(dbName, modulePath);\n} catch (e) {\n  if (String(e).includes('is not authorized to perform action')) {\n    // 403: valid identity, wrong permissions — do NOT retry blindly\n    await spacetrySwitchIdentity(dbOwnerHint);\n    throw new Error(`not owner of '${dbName}' — switch identity or ask owner for access`);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["spacetimedb","auth","http-403","authorization","permissions"],"backgroundTag":"http-403-forbidden","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}