overleaf/overleaf · error · CodedError
ProjectNotFound
ProjectNotFound
Error message
project not found
What it means
joinProject throws CodedError('project not found', 'ProjectNotFound') when the upstream web API returns HTTP 404, and also when the successful response body lacks a project object (the `data && data.project` guard right after). It distinguishes 'no such project' from 'no permission' (403).
Source
Thrown at services/real-time/app/js/WebApiManager.js:44
user: settings.apis.web.user,
password: settings.apis.web.pass,
},
json: {
userId,
anonymousAccessToken: user.anonymousAccessToken,
},
})
} catch (error) {
if (error instanceof RequestFailedError) {
if (error.response.status === 429) {
throw new CodedError(
'rate-limit hit when joining project',
'TooManyRequests'
)
} else if (error.response.status === 403) {
throw new NotAuthorizedError()
} else if (error.response.status === 404) {
throw new CodedError('project not found', 'ProjectNotFound')
}
throw new WebApiRequestFailedError(error.response.status)
}
throw OError.tag(error, 'join project request failed')
}
if (!(data && data.project)) {
throw new CorruptedJoinProjectResponseError()
}
const userMetadata = {
isRestrictedUser: data.isRestrictedUser,
isTokenMember: data.isTokenMember,
isInvitedMember: data.isInvitedMember,
}
return {
project: data.project,
privilegeLevel: data.privilegeLevel,
userMetadata,
}View on GitHub (pinned to 28ad3b03b7)
Solutions
- Confirm the projectId exists via the project list/API before joining
- Refresh the share link from the project owner — the old one may point to a deleted project
- Check you are hitting the correct environment (the id likely doesn't exist in this deployment)
- Handle missing `project` in the response defensively if you control the upstream contract
Example fix
// before
await manager.joinProject(user, req.params.projectId)
// after
const project = await ProjectLocator.promises.getProject(req.params.projectId)
if (!project) return res.status(404).render('projectNotFound')
await manager.joinProject(user, req.params.projectId) Defensive patterns
Strategy: type-guard
Validate before calling
const project = await ProjectLocator.promises.getProject(projectId, { userId })
if (!project) return res.status(404).render('projectNotFound') // before joining Type guard
function hasProjectPayload(d: unknown): d is { project: Record<string, unknown> } {
return typeof d === 'object' && d !== null && 'project' in d && d.project != null
} Try / catch
try {
await manager.joinProject(user, projectId)
} catch (err) {
if (err instanceof CodedError && err.code === 'ProjectNotFound') {
return res.redirect('/project/not-found')
}
throw err
} Prevention
- Validate projectId format (e.g. ObjectId) before the call
- Look up the project locally first to catch deletions early
- Never reuse project ids across environments
- Render friendly 404 pages instead of surfacing the raw error
When it happens
Trigger: Calling joinProject with a projectId that does not exist (deleted project, typo'd id), or a 200 response whose body omits the `project` field.
Common situations: Following a stale share link after the project was deleted or trashed; IDs copied from URLs of another environment (prod vs dev); races where the project is deleted between listing and joining; upstream API version changes altering the response shape.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03).
Data as JSON: /api/errors/b6a5010f7d1558f4.
Report an issue: GitHub.