FuelLabs/sway · error
missing closing parenthesis
Error message
missing closing parenthesis
What it means
parse_pkg_dep_line handles optional parenthesized dependency names: if a line starts with '(', everything up to the first ')' is taken as the name. This error is the guard for a line that opens a parenthesis but never closes it (e.g. '(mydep git+https://...'). Note it is effectively defensive-only: str::split(')') always yields at least one item, so in practice the branch is near-unreachable and a ')' -less line instead mis-parses downstream (surfacing later as an invalid-source or salt error).
Source
Thrown at forc-pkg/src/lock.rs:315
}
type ParsedPkgLine<'a> = (Option<&'a str>, &'a str, Option<fuel_tx::Salt>);
// Parse the given `PkgDepLine` into its dependency name and unique string segments.
//
// I.e. given "(<dep_name>) <name> <source> (<salt>)", returns ("<dep_name>", "<name> <source>", "<salt>").
//
// Note that <source> may not appear in the case it is not required for disambiguation.
fn parse_pkg_dep_line(pkg_dep_line: &str) -> anyhow::Result<ParsedPkgLine> {
let s = pkg_dep_line.trim();
let (dep_name, s) = match s.starts_with('(') {
false => (None, s),
true => {
// If we have the open bracket, grab everything until the closing bracket.
let s = &s["(".len()..];
let mut iter = s.split(')');
let dep_name = iter
.next()
.ok_or_else(|| anyhow!("missing closing parenthesis"))?;
// The rest is the unique package string and possibly the salt.
let s = &s[dep_name.len() + ")".len()..];
(Some(dep_name), s)
}
};
// Check for salt.
let mut iter = s.split('(');
let pkg_str = iter
.next()
.ok_or_else(|| anyhow!("missing pkg string"))?
.trim();
let salt_str = iter.next().map(|s| s.trim()).map(|s| &s[..s.len() - 1]);
let salt = match salt_str {
Some(salt_str) => Some(
fuel_tx::Salt::from_str(salt_str)
.map_err(|e| anyhow!("invalid salt in lock file: {e}"))?,
),View on GitHub (pinned to 47e5e902fa)
Solutions
- Fix the line to '(name source)' or the plain 'name source' form, or delete Forc.lock and regenerate with forc build.
- Prefer regenerating the lock over hand-editing dependency lines.
Example fix
# before (Forc.lock) dependencies = [ "(auth git+https://github.com/x/auth#abc" ] # after dependencies = [ "(auth git+https://github.com/x/auth#abc123...)" ]
Defensive patterns
Strategy: validation
Validate before calling
// Rust, validate a dependency line's bracket balance before parsing:
fn balanced_parens(line: &str) -> bool {
let mut depth = 0i32;
for c in line.chars() { if c=='(' {depth+=1} else if c==')' {depth-=1} }
depth == 0 && !line.trim_start().starts_with('(') || line.contains(')')
} Try / catch
// Anyhow Result: on parse failure of a lock line, regenerate the lock:
if let Err(e) = parse_pkg_dep_line(line) { /* drop Forc.lock and re-resolve */ } Prevention
- Keep dependency lines in the canonical 'name source' or '(name source) salt' shapes.
- Avoid hand-authoring contract-dependency lines in Forc.lock.
- Validate bracket balance when scripts generate lock content.
When it happens
Trigger: A hand-edited or corrupted Forc.lock dependency line beginning with '(' and lacking the matching ')' - the intended, if rarely reachable, target of this guard.
Common situations: Hand-written lock lines imitating the '(name source) salt' contract-dependency form; truncation mid-line; scripts injecting malformed entries.
Related errors
- missing pkg string
- failed to parse dependency "{}": {}
- invalid salt in lock file: {e}
- failed to read {}: {}
- invalid 'source' entry for package {} lock: {:?}
AI-assisted analysis of FuelLabs/sway@47e5e902fa (2026-08-16).
Data as JSON: /api/errors/e336680cc1053884.
Report an issue: GitHub.