gitbutlerapp/gitbutler · error
Expected `project_id` to be either a ProjectHandle or a…
Error message
Expected `project_id` to be either a ProjectHandle or a legacy ProjectId, got '{value}' What it means
ProjectHandleOrLegacyProjectId::from_str parses a project_id string into a ProjectHandle, and (when the 'legacy' feature is enabled) also accepts a legacy numeric LegacyProjectId. This error is thrown when the string is neither a valid ProjectHandle nor a valid legacy ProjectId, i.e. the whole parse chain failed.
Solutions
- Verify the string is a valid ProjectHandle (correct format/characters)
- If using legacy numeric IDs, ensure the value is numeric and the 'legacy' feature is enabled
- Confirm the project exists; obtain a fresh project_id via the project listing API
Example fix
// before
let id = input.parse::<ProjectHandleOrLegacyProjectId>()?; // input = "my project"
// after
let id = ProjectHandle::new("my-project")?.parse::<ProjectHandleOrLegacyProjectId>()?; Defensive patterns
Strategy: validation
Validate before calling
fn parse_project_id(raw: &str) -> anyhow::Result<ProjectHandleOrLegacyProjectId> {
anyhow::ensure!(!raw.trim().is_empty(), "project_id must not be empty");
raw.parse::<ProjectHandleOrLegacyProjectId>().map_err(Into::into)
} Type guard
fn is_legacy_id(s: &str) -> bool { s.parse::<u64>().is_ok() } Try / catch
match raw.parse::<ProjectHandleOrLegacyProjectId>() {
Ok(id) => id,
Err(e) => return Err(anyhow::anyhow!("bad project_id '{raw}': {e}")),
} Prevention
- Always obtain project ids from the project listing API rather than free text
- Validate handle format (allowed characters) before parsing
- Document that legacy numeric ids require the 'legacy' feature
When it happens
Trigger: Deserializing or parsing a project_id with a string that is neither a valid handle nor a numeric legacy id — e.g. an empty string, a malformed handle, or a non-numeric legacy id like 'abc123' when feature "legacy" is compiled in.
Common situations: Users passing a human-readable project name containing invalid characters, stale IDs from an older data format, or API callers sending arbitrary strings as project_id.
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 a ProjectHandle, got
- Unknown app suffix ' ', expected one of: dev, nightly…
- commit prefix ' ' is ambiguous
- commit ' ' is not part of the editable divergence
- Could not find range from
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/01fca6e49b67a763.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-project-handle/src/project_handle.rs:157
{
let value = <String as serde::Deserialize>::deserialize(deserializer)?;
value.parse().map_err(serde::de::Error::custom)
}
}
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)
}
}View on GitHub (pinned to 58e5313667)