multica-ai/multica · error

resolve --attachment path %q: %w

Error message

resolve --attachment path %q: %w

What it means

While enforcing that a local --attachment path stays within the working directory, the attachment variant of the workdir guard could not resolve the path at all (fileWithinWorkingDir errored). The %w wraps the OS-level cause from resolution — dangling symlink, permission-denied traversal, symlink loop, or similar. URL-valued attachments never reach this code (they're filtered by isHTTPURL earlier).

Source

Thrown at server/cmd/multica/cmd_issue.go:991

func isHTTPURL(path string) bool {
	p := strings.TrimSpace(path)
	return strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://")
}

// ensureAttachmentWithinWorkdir applies the same workdir containment guard as
// --description-file / --content-file (MUL-4252) to a local --attachment path.
// An agent that writes a chart/report to a machine-shared path like /tmp and
// then attaches it could otherwise pick up another run's — possibly another
// workspace's — stale file (the image version of the /tmp/desc.md leak). URL
// values are filtered by the caller and never reach here. --allow-external-file
// overrides, mirroring the text-flag escape hatch.
func ensureAttachmentWithinWorkdir(cmd *cobra.Command, filePath string) error {
	if allow, _ := cmd.Flags().GetBool("allow-external-file"); allow {
		return nil
	}
	within, err := fileWithinWorkingDir(filePath)
	if err != nil {
		return fmt.Errorf("resolve --attachment path %q: %w", filePath, err)
	}
	if !within {
		return fmt.Errorf(
			"--attachment path %q resolves outside the current working directory; "+
				"attach files generated inside the task workdir rather than machine-shared "+
				"paths like /tmp, where another run's stale file can be attached by mistake. "+
				"Pass --allow-external-file to override.",
			filePath)
	}
	return nil
}

// pendingAttachment is a local --attachment file that passed URL filtering and
// the workdir guard and has been read into memory, ready to upload.
type pendingAttachment struct {
	path string
	data []byte
}

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Read the wrapped OS error for the concrete cause (ENOENT, EACCES, ELOOP).
  2. Resolve the symlink chain (realpath) and attach the real file, placed inside the workdir.
  3. Regenerate the artifact if the target is missing.
  4. If the outside path is intentional, pass --allow-external-file.

Example fix

# before (dangling artifact symlink)
multica issue create --title T --attachment ./latest.png   # -> missing target
# after
ls build/   # confirm the real file
multica issue create --title T --attachment ./build/chart-v2.png
Defensive patterns

Strategy: validation

Validate before calling

# attachment must resolve to a real file inside the CWD
 p=$(realpath -e "$ATTACH" 2>/dev/null) || { echo "unresolvable attachment: $ATTACH" >&2; exit 1; }
case "$p" in "$(realpath .)"/*) ;; *) echo "attachment outside workdir" >&2; exit 1;; esac

Prevention

When it happens

Trigger: Running `multica issue create --attachment <path>` where <path> is a broken symlink, sits behind a stale mount, or cannot be stat'd by the resolving user — and --allow-external-file is not set.

Common situations: Artifact symlinks (latest.png -> build/output.png) where the target was cleaned up; containerized runs with different mount visibility; CI artifacts copied as dangling symlinks.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/bc8668556f3f88a9. Report an issue: GitHub.