{"record":{"id":"5040dc08785b8ea3","repo":"Graphify-Labs/graphify","slug":"gws-export-failed-for-file-id-stderr","errorCode":null,"errorMessage":"gws export failed for {file_id}: {stderr}","messagePattern":"gws export failed for (.+?): (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"graphify/google_workspace.py","lineNumber":121,"sourceCode":"    # Drive resource keys are sent via X-Goog-Drive-Resource-Keys. The current\n    # gws export command has no custom-header flag, so do not pass resourceKey\n    # as an unsupported query parameter.\n    _ = resource_key\n    output = output.resolve()\n    output.parent.mkdir(parents=True, exist_ok=True)\n    timeout = int(os.environ.get(\"GRAPHIFY_GOOGLE_WORKSPACE_TIMEOUT\", \"120\"))\n    result = subprocess.run(\n        [exe, \"drive\", \"files\", \"export\", \"--params\", json.dumps(params), \"-o\", output.name],\n        capture_output=True,\n        cwd=output.parent,\n        text=True,\n        timeout=timeout,\n    )\n    if result.returncode != 0:\n        stderr = (result.stderr or result.stdout or \"\").strip()\n        if len(stderr) > 1200:\n            stderr = stderr[:1200] + \"...\"\n        raise RuntimeError(f\"gws export failed for {file_id}: {stderr}\")\n\n\ndef _sidecar_path(path: Path, out_dir: Path, root: \"Path | None\" = None) -> Path:\n    # Hash the scan-root-relative, NFC-normalized path — not the absolute path.\n    # The absolute form salts the sidecar name with the checkout location, so the\n    # same shortcut in two clones/worktrees emits differently-named byte-identical\n    # sidecars, each ingested as a distinct source doc when graphify-out/ is\n    # committed (#2059; mirrors convert_office_file). NFC guards macOS NFD drift\n    # (#1226). The relative path still disambiguates same-stem files.\n    import unicodedata\n    if root is None:\n        root = out_dir.parent.parent\n    try:\n        key = path.resolve().relative_to(Path(root).resolve()).as_posix()\n    except (ValueError, OSError):\n        key = str(path.resolve())\n    name_hash = hashlib.sha256(unicodedata.normalize(\"NFC\", key).encode()).hexdigest()[:8]\n    return out_dir / f\"{path.stem}_{name_hash}.md\"","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/google_workspace.py#L103-L139","documentation":"Raised by _run_gws_export when the `gws drive files export` subprocess exits non-zero. graphify captures stderr (falling back to stdout), truncates it to 1200 chars to keep the message readable, and raises a RuntimeError naming the file_id so the failing Drive document is identifiable in a batch scan.","triggerScenarios":"gws is installed and runs, but export fails: expired/missing Drive auth token, 403/404 on the file ID, resource-key-protected file, Drive quota, or the per-run timeout env (GRAPHIFY_GOOGLE_WORKSPACE_TIMEOUT, default 120s) exceeded via subprocess timeout semantics surfacing as failure output.","commonSituations":"Auth token expired since the last `gws auth login`; shortcut points to a deleted or permission-restricted doc; shared drive files needing extra scopes; exporting a large spreadsheet past the 120s default timeout.","solutions":["Read the embedded stderr — it names the actual cause (auth vs permissions vs not-found)","Re-authenticate: `gws auth login -s drive`","For slow/oversized exports, raise the timeout: GRAPHIFY_GOOGLE_WORKSPACE_TIMEOUT=300 graphify ...","If the file ID is dead or inaccessible, delete/move the shortcut or fix sharing in Drive"],"exampleFix":"# before\ngws auth token expires\ngraphify build .   # RuntimeError: gws export failed for 1AbC...: Request had invalid credentials\n\n# after\ngws auth login -s drive\ngraphify build .","handlingStrategy":"retry","validationCode":"import shutil, subprocess\n\ndef gws_ready() -> bool:\n    exe = shutil.which(\"gws\")\n    if exe is None:\n        return False\n    return subprocess.run([exe, \"auth\", \"status\"], capture_output=True).returncode == 0","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        export_google_shortcut(path, out_dir)\n        break\n    except RuntimeError as e:\n        if \"gws export failed\" not in str(e):\n            raise\n        if attempt == 2 or \"credentials\" in str(e) or \"403\" in str(e):\n            raise  # non-transient: auth/permission - do not retry\n        import time; time.sleep(2 ** attempt)","preventionTips":["Run `gws auth login -s drive` as part of environment setup and periodically re-auth","Set GRAPHIFY_GOOGLE_WORKSPACE_TIMEOUT generously for large spreadsheets","Distinguish auth/permission failures (fix config) from transient 5xx (retry) by parsing the embedded stderr"],"tags":["google-workspace","subprocess","authentication","external-cli","ingest"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}