gitbutlerapp/gitbutler · error
Expected `project_id` to be a ProjectHandle, got
Error message
Expected `project_id` to be a ProjectHandle, got '{value}' What it means
This is the non-legacy variant of the ProjectHandleOrLegacyProjectId parse failure. When the crate is built without the 'legacy' feature, only a ProjectHandle is accepted; this error is thrown for any string that fails ProjectHandle parsing. The legacy ProjectId path does not exist in this build.
Solutions
- Send a valid ProjectHandle string instead of a legacy numeric ProjectId
- Rebuild/reconfigure with the 'legacy' feature enabled if legacy IDs must be accepted
- Update callers to the current API that returns project handles
Example fix
// before let id = "42".parse::<ProjectHandleOrLegacyProjectId>()?; // after let id = "my-project-handle".parse::<ProjectHandleOrLegacyProjectId>()?;
Defensive patterns
Strategy: validation
Validate before calling
fn ensure_project_handle(raw: &str) -> anyhow::Result<ProjectHandleOrLegacyProjectId> {
anyhow::ensure!(raw.parse::<u64>().is_err(), "legacy numeric project ids are not supported; use a project handle");
raw.parse::<ProjectHandleOrLegacyProjectId>().map_err(Into::into)
} Try / catch
let id = raw.parse::<ProjectHandleOrLegacyProjectId>()
.with_context(|| format!("expected project handle, got '{raw}'"))?; Prevention
- Migrate all clients to ProjectHandle strings; stop emitting legacy numeric ids
- Confirm feature flags ('legacy') match between build and data format
- Validate ids at deserialization boundaries with helpful context
When it happens
Trigger: Parsing/deserializing a project_id (e.g. from JSON) that is not a valid ProjectHandle while the crate is compiled with default features (no 'legacy'), such as a bare numeric id like '42'.
Common situations: Clients still sending legacy numeric project IDs after a migration to handles; payloads serialized by an older client version against a newer server.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Expected `project_id` to be either a ProjectHandle or a…
- commit prefix ' ' is ambiguous
- commit ' ' is not part of the editable divergence
- Could not find range from
- Failed to migrate GitLab token for project
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/49c77a6e04c36dd0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-project-handle/src/project_handle.rs:161
}
impl std::str::FromStr for ProjectHandleOrLegacyProjectId {
type Err = anyhow::Error;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if let Ok(handle) = value.parse::<ProjectHandle>() {
return Ok(Self::ProjectHandle(handle));
}
#[cfg(feature = "legacy")]
if let Ok(project_id) = value.parse::<LegacyProjectId>() {
return Ok(Self::LegacyProjectId(project_id));
}
#[cfg(feature = "legacy")]
return Err(anyhow::anyhow!(
"Expected `project_id` to be either a ProjectHandle or a legacy ProjectId, got '{value}'"
));
#[cfg(not(feature = "legacy"))]
return Err(anyhow::anyhow!(
"Expected `project_id` to be a ProjectHandle, got '{value}'"
));
}
}
impl<'de> serde::Deserialize<'de> for ProjectHandleOrLegacyProjectId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <String as serde::Deserialize>::deserialize(deserializer)?;
value.parse().map_err(serde::de::Error::custom)
}
}
impl serde::Serialize for ProjectHandleOrLegacyProjectId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
whereView on GitHub (pinned to 58e5313667)