{"record":{"id":"0043573afa967ebe","repo":"larksuite/cli","slug":"binary-not-executable-w","errorCode":null,"errorMessage":"binary not executable: %w","messagePattern":"binary not executable: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/selfupdate/updater.go","lineNumber":452,"sourceCode":"\t// Prefer PATH resolution so npm global bin symlinks pick up the newly\n\t// installed binary (#836). If `lark-cli` is not on PATH (e.g. the user\n\t// invoked this process by absolute path), fall back to the running\n\t// executable — same as the pre-#836 secondary resolution path.\n\texe, err := execLookPath(\"lark-cli\")\n\tif err != nil {\n\t\texe, err = vfs.Executable()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot locate binary: %w\", err)\n\t\t}\n\t}\n\tctx, cancel := context.WithTimeout(context.Background(), verifyTimeout)\n\tdefer cancel()\n\tout, err := exec.CommandContext(ctx, exe, \"--version\").Output()\n\tif ctx.Err() == context.DeadlineExceeded {\n\t\treturn fmt.Errorf(\"binary verification timed out after %s\", verifyTimeout)\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"binary not executable: %w\", err)\n\t}\n\tfields := strings.Fields(strings.TrimSpace(string(out)))\n\tif len(fields) == 0 {\n\t\treturn fmt.Errorf(\"empty version output\")\n\t}\n\tactual := strings.TrimPrefix(fields[len(fields)-1], \"v\")\n\texpected := strings.TrimPrefix(expectedVersion, \"v\")\n\tif actual != expected {\n\t\treturn fmt.Errorf(\"expected version %s, got %q\", expectedVersion, actual)\n\t}\n\treturn nil\n}\n\n// Truncate returns the last maxLen runes of s.\nfunc Truncate(s string, maxLen int) string {\n\tif maxLen <= 0 {\n\t\treturn \"\"\n\t}","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/selfupdate/updater.go#L434-L470","documentation":"VerifyBinary runs the newly downloaded binary with `--version` under a context timeout and wraps any exec failure as \"binary not executable\". The library throws this because a self-update must prove the replacement binary actually runs before swapping it in; if exec.CommandContext cannot start or complete the process, the download is rejected as unsafe to install. The wrapped cause distinguishes ENOENT, permission, format, and timeout-style failures.","triggerScenarios":"exec.CommandContext(ctx, exe, \"--version\").Output() returns a non-nil error: the downloaded file is not executable (missing +x), is corrupt/truncated, is built for the wrong OS/arch, a dynamic linker is missing, or the process fails/exits non-zero during verification.","commonSituations":"Failed or partial download left a truncated file; update artifact published for the wrong platform; a CI pipeline stripped the executable bit; antivirus quarantined the freshly written binary; the binary crashes on startup in the target environment.","solutions":["Re-download the release and verify its checksum; the file is likely truncated or corrupt","chmod +x the downloaded binary before invoking the updater (or fix the packaging step that sets permissions)","Confirm the artifact matches GOOS/GOARCH of the host","Run the downloaded binary manually with --version to see the raw exec error","Check whether antivirus/EDR is quarantining newly written executables"],"exampleFix":"// before (downloaded but not executable)\nbin downloaded, exec fails: \"binary not executable: fork/exec /tmp/...: permission denied\"\n// after\nif err := os.Chmod(binPath, 0o755); err != nil { return err }\n// then proceed with update.VerifyBinary(...)","handlingStrategy":"validation","validationCode":"info, err := os.Stat(binPath)\nif err != nil || info.Mode().Perm()&0o111 == 0 {\n    return fmt.Errorf(\"downloaded binary is not executable: %s\", binPath)\n}\n// sanity-run it before update\nif out, err := exec.Command(binPath, \"--version\").Output(); err != nil {\n    return fmt.Errorf(\"pre-check failed: %w\", err)\n} else { _ = out }","typeGuard":null,"tryCatchPattern":"if err := update.VerifyBinary(ctx, path, expectedVersion); err != nil {\n    var execErr *exec.ExitError\n    if errors.As(err, &execErr) { log.Printf(\"verify exit error: %v\", execErr.Stderr) }\n    return fmt.Errorf(\"update aborted, keeping current binary: %w\", err)\n}","preventionTips":["Verify download checksums against release manifests","Set the executable bit atomically in the download/temp-file path","Smoke-test binaries in CI on the exact GOOS/GOARCH matrix","Keep antivirus exclusions for the updater's temp directory"],"tags":["selfupdate","exec","permission","binary"],"backgroundTag":"binary-not-executable","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}