hcengineering/platform · error · ApiError
Forbidden: read-only token
Error message
Forbidden: read-only token
What it means
After decoding the auth token, the pod-export server rejects requests whose token carries an extra.readonly flag (any value). This HTTP 403 error exists because export creates data in the target workspace, which a read-only token is not permitted to do.
Source
Thrown at services/export/pod-export/src/server.ts:509
measureCtx.warn(`Invalid targetWorkspace parameter: ${String(targetWorkspace)}`)
throw new ApiError(400, 'Missing or invalid required parameter: targetWorkspace')
}
if (_class == null || typeof _class !== 'string') {
measureCtx.warn(`Invalid _class parameter: ${String(_class)}`)
throw new ApiError(400, 'Missing or invalid required parameter: _class')
}
if (conflictStrategy !== undefined && conflictStrategy !== 'skip' && conflictStrategy !== 'duplicate') {
measureCtx.warn(`Invalid conflictStrategy: ${String(conflictStrategy)}`)
throw new ApiError(400, 'Invalid conflictStrategy. Must be "skip" or "duplicate"')
}
if (includeAttachments !== undefined && typeof includeAttachments !== 'boolean') {
measureCtx.warn(`Invalid includeAttachments: ${String(includeAttachments)}`)
throw new ApiError(400, 'Invalid includeAttachments. Must be boolean')
}
decodedToken = decodeToken(token)
if (decodedToken.extra?.readonly !== undefined) {
throw new ApiError(403, 'Forbidden: read-only token')
}
// Get target workspace info
const accountClient = getClient(envConfig.AccountsUrl, token)
const targetWsLoginInfo = await accountClient.getLoginWithWorkspaceInfo()
const targetWsInfo = targetWsLoginInfo.workspaces[targetWorkspace]
if (targetWsInfo === undefined) {
measureCtx.warn(`Target workspace not found or not accessible: ${targetWorkspace}`)
throw new ApiError(404, 'Target workspace not found or not accessible')
}
// Verify user has write access to target workspace
const isAdmin: boolean = decodedToken.extra?.admin === 'true'
if (!isAdmin && targetWsInfo.role !== AccountRole.Owner) {
measureCtx.warn(
`User does not have write access to target workspace: ${targetWorkspace}, role: ${targetWsInfo.role}`
)View on GitHub (pinned to 63e28dc964)
Solutions
- Obtain a non-read-only token (mint a new one without the readonly extra flag) and retry.
- Use a full-permission account/token for cross-workspace export operations.
- If the readonly flag is wrong, regenerate the token with correct scope configuration.
Example fix
// before curl -H 'Authorization: Bearer <readonly-token>' .../export // after curl -H 'Authorization: Bearer <token-with-write-scope>' .../export
Defensive patterns
Strategy: try-catch
Validate before calling
const decoded = decodeToken(token)
if (decoded?.extra?.readonly !== undefined) {
throw new Error('Refusing to call export with a read-only token')
} Type guard
function isWritableToken(decoded: DecodedToken): boolean {
return decoded.extra?.readonly === undefined
} Try / catch
try {
await exportPod({ token })
} catch (err) {
if (err instanceof ApiError && err.status === 403 && /read-only/i.test(err.message)) {
console.error('Swap to a token without the readonly flag and retry')
} else throw err
} Prevention
- Keep read-only and write credentials in separate config keys and use the right one per operation.
- Inspect decoded.extra.readonly at startup of migration scripts before doing work.
- Don't reuse monitoring/reporting tokens for migration tooling.
When it happens
Trigger: Authenticating the export call with a token whose decoded `extra.readonly` property is defined (e.g. a read-only service account or restricted key), regardless of the actual flag value ('true' or even 'false').
Common situations: Ops scripts using a deliberately restricted read-only key to run an import/migration into another workspace; reusing a shared read-only token from monitoring/reporting tooling; a stale token minted with readonly scope.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- platform.status.Forbidden
- You do not have write access to the target workspace. Owner
- Forbidden
- Not an owner of workspace
- Error building local installation folder (${path__WEBPACK_IM
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/b6f31ef65886d7f4.
Report an issue: GitHub.