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

  1. Confirm the projectId exists via the project list/API before joining
  2. Refresh the share link from the project owner — the old one may point to a deleted project
  3. Check you are hitting the correct environment (the id likely doesn't exist in this deployment)
  4. 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

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.